From 170c121a1dfa9cc8f11f9156d278982f17b40c8b Mon Sep 17 00:00:00 2001 From: REAndroid Date: Thu, 21 Sep 2023 15:22:46 +0200 Subject: [PATCH] [DEX] Optimize performance --- .../com/reandroid/dex/base/DexBlockItem.java | 17 ++ .../com/reandroid/dex/common/DexUtils.java | 43 ++++ .../dex/header/Alder32OutputStream.java | 47 ---- .../com/reandroid/dex/header/Checksum.java | 16 +- .../com/reandroid/dex/header/DexHeader.java | 28 +-- .../dex/header/Sha1OutputStream.java | 52 ----- .../com/reandroid/dex/header/Signature.java | 17 +- .../java/com/reandroid/dex/index/ClassId.java | 21 +- .../java/com/reandroid/dex/index/FieldId.java | 106 ++++----- .../reandroid/dex/index/IndexItemEntry.java | 8 - .../dex/index/ItemIndexReference.java | 16 +- .../dex/index/ItemOffsetReference.java | 14 +- .../com/reandroid/dex/index/MethodId.java | 88 ++++--- .../java/com/reandroid/dex/index/ProtoId.java | 31 ++- .../reandroid/dex/index/StringReference.java | 29 ++- .../java/com/reandroid/dex/index/TypeId.java | 63 ++--- .../reandroid/dex/item/AnnotationElement.java | 8 +- .../reandroid/dex/item/AnnotationGroup.java | 5 +- .../reandroid/dex/item/AnnotationItem.java | 16 +- .../dex/item/AnnotationsDirectory.java | 9 + .../com/reandroid/dex/item/DataItemEntry.java | 16 +- .../reandroid/dex/item/DexContainerItem.java | 8 + .../com/reandroid/dex/item/EncodedArray.java | 16 +- .../java/com/reandroid/dex/item/FieldDef.java | 11 +- .../dex/item/IntegerOffsetSectionList.java | 85 +++++++ .../com/reandroid/dex/item/StringData.java | 5 +- .../java/com/reandroid/dex/item/TypeList.java | 47 +++- .../com/reandroid/dex/model/DexClass.java | 110 ++++++--- .../com/reandroid/dex/model/DexField.java | 59 +++-- .../java/com/reandroid/dex/model/DexFile.java | 219 ++++++------------ .../com/reandroid/dex/model/DexMethod.java | 53 +++-- .../com/reandroid/dex/pool/DexIdPool.java | 2 +- .../com/reandroid/dex/refactor/Rename.java | 8 +- .../reandroid/dex/sections/DexFileBlock.java | 197 ++++++++++++++++ .../com/reandroid/dex/sections/MapItem.java | 3 +- .../com/reandroid/dex/sections/Marker.java | 7 +- .../com/reandroid/dex/sections/Section.java | 23 ++ .../reandroid/dex/sections/SectionList.java | 8 +- .../com/reandroid/dex/value/ArrayValue.java | 17 +- .../com/reandroid/dex/value/BooleanValue.java | 8 +- .../com/reandroid/dex/value/CharValue.java | 51 ++-- .../{DexValue.java => DexValueBlock.java} | 11 +- .../com/reandroid/dex/value/DexValueType.java | 8 +- .../com/reandroid/dex/value/NullValue.java | 6 +- .../reandroid/dex/value/PrimitiveValue.java | 8 +- .../com/reandroid/dex/value/SectionValue.java | 11 +- .../com/reandroid/dex/value/StringValue.java | 9 + 47 files changed, 1012 insertions(+), 628 deletions(-) delete mode 100644 src/main/java/com/reandroid/dex/header/Alder32OutputStream.java delete mode 100644 src/main/java/com/reandroid/dex/header/Sha1OutputStream.java create mode 100644 src/main/java/com/reandroid/dex/sections/DexFileBlock.java rename src/main/java/com/reandroid/dex/value/{DexValue.java => DexValueBlock.java} (88%) diff --git a/src/main/java/com/reandroid/dex/base/DexBlockItem.java b/src/main/java/com/reandroid/dex/base/DexBlockItem.java index 1ebcab3e1..e1fafd4f1 100644 --- a/src/main/java/com/reandroid/dex/base/DexBlockItem.java +++ b/src/main/java/com/reandroid/dex/base/DexBlockItem.java @@ -20,6 +20,7 @@ import com.reandroid.arsc.item.IntegerReference; import com.reandroid.dex.io.ByteReader; import com.reandroid.dex.io.StreamUtil; +import com.reandroid.dex.pool.DexIdPool; import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionList; import com.reandroid.dex.sections.SectionType; @@ -33,6 +34,14 @@ public DexBlockItem(int bytesLength) { super(bytesLength); } + public T1 createOffsetItem(SectionType sectionType) { + Section section = getSection(sectionType); + if(section != null){ + return section.createOffsetItem(); + } + return null; + } + public T1 getAt(SectionType sectionType, IntegerReference offset){ if(offset != null){ return getAt(sectionType, offset.get()); @@ -84,6 +93,14 @@ public Section getSection(SectionType sectionType){ return null; } + public DexIdPool getPool(SectionType sectionType){ + Section section = getSection(sectionType); + if(section != null){ + return section.getPool(); + } + return null; + } + public static int writeUleb128(byte[] bytes, int offset, int value) { int index = 0; while ((value & 0xffffffffL) > 0x7f) { diff --git a/src/main/java/com/reandroid/dex/common/DexUtils.java b/src/main/java/com/reandroid/dex/common/DexUtils.java index 2596eac19..9f6b58db6 100644 --- a/src/main/java/com/reandroid/dex/common/DexUtils.java +++ b/src/main/java/com/reandroid/dex/common/DexUtils.java @@ -17,7 +17,50 @@ // originally copied from JesusFreke/smali package com.reandroid.dex.common; +import java.io.IOException; + public class DexUtils { + + public static String quoteString(String text){ + StringBuilder builder = new StringBuilder(text.length() + 2); + try { + appendQuotedString(builder, text); + } catch (IOException ignored) { + } + return builder.toString(); + } + public static void appendQuotedString(Appendable appendable, String text) throws IOException { + appendable.append('"'); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + + if ((c >= ' ') && (c < 0x7f)) { + if ((c == '\'') || (c == '\"') || (c == '\\')) { + appendable.append('\\'); + } + appendable.append(c); + continue; + } else if (c <= 0x7f) { + switch (c) { + case '\n': + appendable.append("\\n"); + continue; + case '\r': + appendable.append("\\r"); + continue; + case '\t': + appendable.append("\\t"); + continue; + } + } + appendable.append("\\u"); + appendable.append(Character.forDigit(c >> 12, 16)); + appendable.append(Character.forDigit((c >> 8) & 0x0f, 16)); + appendable.append(Character.forDigit((c >> 4) & 0x0f, 16)); + appendable.append(Character.forDigit(c & 0x0f, 16)); + } + appendable.append('"'); + } public static boolean isNative(String type){ if(type == null){ return false; diff --git a/src/main/java/com/reandroid/dex/header/Alder32OutputStream.java b/src/main/java/com/reandroid/dex/header/Alder32OutputStream.java deleted file mode 100644 index a925f42a4..000000000 --- a/src/main/java/com/reandroid/dex/header/Alder32OutputStream.java +++ /dev/null @@ -1,47 +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.header; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.zip.Adler32; - -public class Alder32OutputStream extends OutputStream { - - private final Adler32 adler32; - - public Alder32OutputStream() { - adler32 = new Adler32(); - } - - @Override - public void write(byte[] bytes, int offset, int length) throws IOException { - if (length != 0) { - adler32.update(bytes, offset, length); - } - } - @Override - public void write(byte[] bytes) throws IOException { - this.write(bytes, 0, bytes.length); - } - @Override - public void write(int i) throws IOException { - this.write(new byte[]{(byte) i}, 0, 1); - } - public int getValue() { - return (int) adler32.getValue(); - } -} diff --git a/src/main/java/com/reandroid/dex/header/Checksum.java b/src/main/java/com/reandroid/dex/header/Checksum.java index df09df726..f2c8b6495 100644 --- a/src/main/java/com/reandroid/dex/header/Checksum.java +++ b/src/main/java/com/reandroid/dex/header/Checksum.java @@ -19,6 +19,9 @@ import com.reandroid.utils.HexUtil; import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.zip.Adler32; public class Checksum extends HeaderPiece { @@ -33,14 +36,11 @@ public void setChecksum(int checksum){ setSize(4); putInteger(0, checksum); } - public void update(Block parent) { - Alder32OutputStream outputStream = new Alder32OutputStream(); - try { - parent.writeBytes(outputStream); - } catch (IOException ex) { - throw new IllegalArgumentException(ex); - } - setChecksum(outputStream.getValue()); + public void update(Block parent, byte[] bytes) { + int start = parent.countUpTo(this) + countBytes(); + Adler32 adler32 = new Adler32(); + adler32.update(bytes, start, bytes.length - start); + setChecksum((int) adler32.getValue()); } @Override diff --git a/src/main/java/com/reandroid/dex/header/DexHeader.java b/src/main/java/com/reandroid/dex/header/DexHeader.java index a9f315cd7..ed8d1b394 100644 --- a/src/main/java/com/reandroid/dex/header/DexHeader.java +++ b/src/main/java/com/reandroid/dex/header/DexHeader.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; public class DexHeader extends FixedDexContainer implements OffsetSupplier, BlockLoad { @@ -106,10 +105,11 @@ public DexHeader(){ } public void updateHeaderInternal(Block parent){ + byte[] bytes = parent.getBytes(); headerSize.set(countBytes()); - fileSize.set(parent.countBytes()); - signature.update(parent); - checksum.update(parent); + fileSize.set(bytes.length); + signature.update(parent, bytes); + checksum.update(parent, bytes); } @Override @@ -128,26 +128,6 @@ public void onBlockLoaded(BlockReader reader, Block sender) throws IOException { } } - @Override - public int onWriteBytes(OutputStream stream) throws IOException { - int start = 0; - Class streamClass = stream.getClass(); - if(streamClass == Alder32OutputStream.class){ - start = 3; - }else if(streamClass == Sha1OutputStream.class){ - start = 4; - } - int result = 0; - Block[] childes = getChildes(); - for(int i = start; i < childes.length; i++){ - Block block = childes[i]; - if(block != null){ - result += block.writeBytes(stream); - } - } - return result; - } - @Override public String toString() { return "Header {" + diff --git a/src/main/java/com/reandroid/dex/header/Sha1OutputStream.java b/src/main/java/com/reandroid/dex/header/Sha1OutputStream.java deleted file mode 100644 index 3fbddf313..000000000 --- a/src/main/java/com/reandroid/dex/header/Sha1OutputStream.java +++ /dev/null @@ -1,52 +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.header; - -import java.io.IOException; -import java.io.OutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class Sha1OutputStream extends OutputStream { - - private final MessageDigest messageDigest; - - public Sha1OutputStream() { - try { - messageDigest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException ex) { - throw new IllegalArgumentException(ex); - } - } - - @Override - public void write(byte[] bytes, int offset, int length) throws IOException { - if (length != 0) { - messageDigest.update(bytes, offset, length); - } - } - @Override - public void write(byte[] bytes) throws IOException { - this.write(bytes, 0, bytes.length); - } - @Override - public void write(int i) throws IOException { - this.write(new byte[]{(byte) i}, 0, 1); - } - public byte[] digest() { - return messageDigest.digest(); - } -} diff --git a/src/main/java/com/reandroid/dex/header/Signature.java b/src/main/java/com/reandroid/dex/header/Signature.java index c594e08f1..d854b82b9 100644 --- a/src/main/java/com/reandroid/dex/header/Signature.java +++ b/src/main/java/com/reandroid/dex/header/Signature.java @@ -16,22 +16,27 @@ package com.reandroid.dex.header; import com.reandroid.arsc.base.Block; +import com.reandroid.dex.model.DexFile; import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; public class Signature extends HeaderPiece{ public Signature(){ super(20); } - public void update(Block parent) { - Sha1OutputStream outputStream = new Sha1OutputStream(); + public void update(Block parent, byte[] bytes) { + int start = parent.countUpTo(this) + countBytes(); + MessageDigest messageDigest; try { - parent.writeBytes(outputStream); - } catch (IOException ex) { + messageDigest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { throw new IllegalArgumentException(ex); } - byte[] bytes = outputStream.digest(); - putByteArray(0, bytes); + messageDigest.update(bytes, start, bytes.length - start); + byte[] digest = messageDigest.digest(); + putByteArray(0, digest); } @Override public String toString() { diff --git a/src/main/java/com/reandroid/dex/index/ClassId.java b/src/main/java/com/reandroid/dex/index/ClassId.java index 1a7268428..916c64c01 100644 --- a/src/main/java/com/reandroid/dex/index/ClassId.java +++ b/src/main/java/com/reandroid/dex/index/ClassId.java @@ -19,7 +19,7 @@ import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.item.*; import com.reandroid.dex.sections.SectionType; -import com.reandroid.dex.value.DexValue; +import com.reandroid.dex.value.DexValueBlock; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; @@ -74,7 +74,10 @@ public TypeId getSuperClass(){ return superClass.getItem(); } public void setSuperClass(TypeId typeId){ - superClass.setItem(typeId); + this.superClass.setItem(typeId); + } + public void setSuperClass(String superClass){ + this.superClass.setItem(superClass); } public StringData getSourceFile(){ return sourceFile.getItem(); @@ -82,6 +85,9 @@ public StringData getSourceFile(){ public void setSourceFile(StringData stringData){ this.sourceFile.setItem(stringData); } + public void setSourceFile(String sourceFile){ + this.sourceFile.setString(sourceFile); + } public TypeId[] getInterfaceTypeIds(){ TypeList interfaceList = getInterfaces(); if(interfaceList != null){ @@ -92,6 +98,9 @@ public TypeId[] getInterfaceTypeIds(){ public TypeList getInterfaces(){ return interfaces.getItem(); } + public ItemOffsetReference getInterfacesReference() { + return this.interfaces; + } public void setInterfaces(TypeList interfaces){ this.interfaces.setItem(interfaces); } @@ -102,6 +111,12 @@ public AnnotationSet getClassAnnotations(){ } return null; } + public void setClassAnnotations(AnnotationSet annotationSet){ + AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); + if(annotationsDirectory != null){ + annotationsDirectory.setClassAnnotations(annotationSet); + } + } public AnnotationsDirectory getAnnotationsDirectory(){ return annotationsDirectory.getItem(); } @@ -117,7 +132,7 @@ public void setClassData(ClassData classData){ public EncodedArray getStaticValues(){ return staticValues.getItem(); } - public DexValue getStaticValue(int i){ + public DexValueBlock getStaticValue(int i){ EncodedArray encodedArray = getStaticValues(); if(encodedArray != null){ return encodedArray.get(i); diff --git a/src/main/java/com/reandroid/dex/index/FieldId.java b/src/main/java/com/reandroid/dex/index/FieldId.java index b5440dd71..7fe743317 100644 --- a/src/main/java/com/reandroid/dex/index/FieldId.java +++ b/src/main/java/com/reandroid/dex/index/FieldId.java @@ -16,7 +16,6 @@ package com.reandroid.dex.index; import com.reandroid.dex.item.StringData; -import com.reandroid.dex.pool.DexIdPool; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; @@ -35,72 +34,46 @@ public FieldId() { this.nameReference = new StringReference( this, 4, StringData.USAGE_FIELD); } - public StringReference getNameReference() { - return nameReference; - } - public String getName(){ - StringData stringData = getNameString(); - if(stringData != null){ - return stringData.getString(); - } - return null; + public String getName() { + return nameReference.getString(); } public void setName(String name){ - DexIdPool stringPool = getPool(SectionType.STRING_DATA); - StringData stringData = stringPool.getOrCreate(name); - setName(stringData); + this.nameReference.setString(name); } public void setName(StringData stringData){ this.nameReference.setItem(stringData); } - @Override - public String getKey(){ - StringBuilder builder = new StringBuilder(); - TypeId type = getClassType(); - if(type == null){ - return null; - } - StringData stringData = type.getNameData(); - if(stringData == null){ - return null; - } - builder.append(stringData.getString()); - builder.append("->"); - stringData = getNameString(); - if(stringData == null){ - return null; - } - builder.append(stringData.getString()); - return builder.toString(); + public StringData getNameString(){ + return nameReference.getItem(); } - public String key(){ - StringBuilder builder = new StringBuilder(); - StringData stringData = getNameString(); - if(stringData == null){ - return null; - } - builder.append(stringData.getString()); - builder.append(':'); - TypeId type = getFieldType(); - if(type == null){ - return null; - } - stringData = type.getNameData(); - if(stringData == null){ - return null; - } - builder.append(stringData.getString()); - return builder.toString(); + StringReference getNameReference() { + return nameReference; + } + public String getClassName(){ + return classType.getKey(); } public TypeId getClassType(){ return classType.getItem(); } - public StringData getNameString(){ - return nameReference.getItem(); + public void setClassType(TypeId typeId){ + classType.setItem(typeId); + } + public void setClassType(String type) { + classType.setItem(type); + } + + public String getFieldTypeName(){ + return fieldType.getKey(); } public TypeId getFieldType(){ return fieldType.getItem(); } + public void setFieldType(TypeId typeId) { + fieldType.setItem(typeId); + } + public void setFieldType(String type) { + fieldType.setItem(type); + } @Override public void refresh() { @@ -115,6 +88,35 @@ void cacheItems(){ nameReference.getStringId(); } + @Override + public String getKey(){ + return key(false); + } + + public String key(boolean appendFieldType) { + StringBuilder builder = new StringBuilder(); + String type = getClassName(); + if(type == null){ + return null; + } + builder.append(type); + builder.append("->"); + String name = getName(); + if(name == null){ + return null; + } + builder.append(name); + if(appendFieldType) { + builder.append(':'); + String fieldType = getFieldTypeName(); + if(fieldType == null) { + return null; + } + builder.append(fieldType); + } + return builder.toString(); + } + @Override public void append(SmaliWriter writer) throws IOException { getClassType().append(writer); diff --git a/src/main/java/com/reandroid/dex/index/IndexItemEntry.java b/src/main/java/com/reandroid/dex/index/IndexItemEntry.java index ad68c0cf8..1eea26ced 100644 --- a/src/main/java/com/reandroid/dex/index/IndexItemEntry.java +++ b/src/main/java/com/reandroid/dex/index/IndexItemEntry.java @@ -56,12 +56,4 @@ public void onReadBytes(BlockReader reader) throws IOException { super.onReadBytes(reader); cacheItems(); } - - public DexIdPool getPool(SectionType sectionType){ - Section section = getSection(sectionType); - if(section != null){ - return section.getPool(); - } - return null; - } } diff --git a/src/main/java/com/reandroid/dex/index/ItemIndexReference.java b/src/main/java/com/reandroid/dex/index/ItemIndexReference.java index 86bd8452e..250cd7983 100644 --- a/src/main/java/com/reandroid/dex/index/ItemIndexReference.java +++ b/src/main/java/com/reandroid/dex/index/ItemIndexReference.java @@ -20,10 +20,12 @@ import com.reandroid.arsc.item.IndirectItem; import com.reandroid.arsc.item.IntegerReference; import com.reandroid.dex.base.DexBlockItem; +import com.reandroid.dex.base.StringKeyItem; +import com.reandroid.dex.pool.DexIdPool; import com.reandroid.dex.sections.SectionType; public class ItemIndexReference extends IndirectItem - implements IntegerReference, BlockRefresh { + implements IntegerReference, BlockRefresh, StringKeyItem { private final SectionType sectionType; private T item; public ItemIndexReference(SectionType sectionType, DexBlockItem blockItem, int offset) { @@ -31,6 +33,14 @@ public ItemIndexReference(SectionType sectionType, DexBlockItem blockItem, in this.sectionType = sectionType; set(-1); } + @Override + public String getKey(){ + T item = getItem(); + if(item != null){ + return item.getKey(); + } + return null; + } public T getItem() { int i = get(); if(item == null){ @@ -51,6 +61,10 @@ public void setItem(T item) { set(value); this.item = item; } + public void setItem(String item){ + DexIdPool pool = getBlockItem().getPool(sectionType); + setItem(pool.getOrCreate(item)); + } @Override public void set(int value) { diff --git a/src/main/java/com/reandroid/dex/index/ItemOffsetReference.java b/src/main/java/com/reandroid/dex/index/ItemOffsetReference.java index 3755e0631..a49b2f10b 100644 --- a/src/main/java/com/reandroid/dex/index/ItemOffsetReference.java +++ b/src/main/java/com/reandroid/dex/index/ItemOffsetReference.java @@ -34,6 +34,15 @@ public ItemOffsetReference(SectionType sectionType, DexBlockItem blockItem, i this.sectionType = sectionType; } + public T getOrCreate() { + T item = getItem(); + if(item != null) { + return item; + } + item = getBlockItem().createOffsetItem(sectionType); + setItem(item); + return item; + } public T getItem() { int i = get(); if(item == null && i != 0){ @@ -76,8 +85,11 @@ public void refresh() { if(reference != null){ value = reference.get(); } - Block.putInteger(getBytesInternal(), getOffset(), value); + if(value == 0){ + this.item = null; + } } + Block.putInteger(getBytesInternal(), getOffset(), value); } @Override diff --git a/src/main/java/com/reandroid/dex/index/MethodId.java b/src/main/java/com/reandroid/dex/index/MethodId.java index 1b98a2012..707e15521 100644 --- a/src/main/java/com/reandroid/dex/index/MethodId.java +++ b/src/main/java/com/reandroid/dex/index/MethodId.java @@ -21,8 +21,10 @@ import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; +import com.reandroid.utils.collection.EmptyIterator; import java.io.IOException; +import java.util.Iterator; public class MethodId extends IndexItemEntry implements Comparable{ @@ -45,41 +47,69 @@ public String getName(){ return null; } public void setName(String name){ - Section stringSection = getSection(SectionType.STRING_DATA); - DexIdPool pool = stringSection.getPool(); - StringData stringData = pool.getOrCreate(name); - setName(stringData); + nameReference.setString(name); } public void setName(StringData stringData){ this.nameReference.setItem(stringData); } - public String getKey(){ - return getKey(false); + + public StringData getNameString(){ + return nameReference.getItem(); } - public String getKey(boolean appendReturnType){ - StringBuilder builder = new StringBuilder(); - TypeId type = getClassType(); - if(type == null){ - return null; + StringReference getNameReference(){ + return nameReference; + } + + public TypeId getClassType(){ + return classType.getItem(); + } + public String getClassName() { + return classType.getKey(); + } + public int getParametersCount() { + ProtoId protoId = getProto(); + if(protoId != null){ + return protoId.getParametersCount(); } - StringData stringData = type.getNameData(); - if(stringData == null){ - return null; + return 0; + } + public TypeId getParameter(int index) { + ProtoId protoId = getProto(); + if(protoId != null){ + return protoId.getParameter(index); } - builder.append(stringData.getString()); - builder.append("->"); - builder.append(getName()); + return null; + } + public Iterator getParameters(){ ProtoId protoId = getProto(); if(protoId != null){ - builder.append(protoId.getKey(appendReturnType)); + return protoId.getParameters(); } - return builder.toString(); + return EmptyIterator.of(); + } + public ProtoId getProto(){ + return proto.getItem(); + } + + public TypeId getReturnTypeId() { + ProtoId protoId = getProto(); + if(protoId != null){ + return protoId.getReturnTypeId(); + } + return null; } - public String key(){ - return key(true); + + @Override + public String getKey() { + return getKey(false); } - public String key(boolean appendReturnType){ + public String getKey(boolean appendReturnType){ StringBuilder builder = new StringBuilder(); + String type = getClassName(); + if(type == null){ + return null; + } + builder.append(type); builder.append("->"); builder.append(getName()); ProtoId protoId = getProto(); @@ -88,20 +118,6 @@ public String key(boolean appendReturnType){ } return builder.toString(); } - - public TypeId getClassType(){ - return classType.getItem(); - } - public StringData getNameString(){ - return nameReference.getItem(); - } - public StringReference getNameReference(){ - return nameReference; - } - public ProtoId getProto(){ - return proto.getItem(); - } - @Override public void refresh() { classType.refresh(); diff --git a/src/main/java/com/reandroid/dex/index/ProtoId.java b/src/main/java/com/reandroid/dex/index/ProtoId.java index 8ee7717b4..a5f7acf37 100644 --- a/src/main/java/com/reandroid/dex/index/ProtoId.java +++ b/src/main/java/com/reandroid/dex/index/ProtoId.java @@ -20,8 +20,10 @@ import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; +import com.reandroid.utils.collection.EmptyIterator; import java.io.IOException; +import java.util.Iterator; public class ProtoId extends IndexItemEntry implements Comparable { @@ -52,6 +54,27 @@ public String getKey(boolean appendReturnType){ return builder.toString(); } + public int getParametersCount(){ + TypeList typeList = getTypeList(); + if(typeList != null){ + return typeList.size(); + } + return 0; + } + public TypeId getParameter(int index) { + TypeList typeList = getTypeList(); + if(typeList != null){ + return typeList.getTypeId(index); + } + return null; + } + public Iterator getParameters(){ + TypeList typeList = getTypeList(); + if(typeList != null){ + return typeList.iterator(); + } + return EmptyIterator.of(); + } public TypeList getTypeList() { return parameters.getItem(); } @@ -71,14 +94,6 @@ public void setShorty(StringData stringData){ shorty.setItem(stringData); } - public int getParametersCount(){ - TypeList typeList = getTypeList(); - if(typeList != null){ - return typeList.size(); - } - return 0; - } - @Override public void refresh() { shorty.refresh(); diff --git a/src/main/java/com/reandroid/dex/index/StringReference.java b/src/main/java/com/reandroid/dex/index/StringReference.java index 9b6f038f3..767e59813 100644 --- a/src/main/java/com/reandroid/dex/index/StringReference.java +++ b/src/main/java/com/reandroid/dex/index/StringReference.java @@ -22,7 +22,6 @@ import com.reandroid.dex.base.DexBlockItem; import com.reandroid.dex.item.StringData; import com.reandroid.dex.pool.DexIdPool; -import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionType; import com.reandroid.utils.CompareUtil; @@ -81,17 +80,33 @@ public String getString(){ } return null; } - public void setString(String text){ - if(Objects.equals(text, getString())){ + public void setString(String text) { + setString(text, false); + } + public void setString(String text, boolean overwrite) { + StringId stringId = this.stringId; + StringData stringData = null; + if(stringId != null){ + stringData = stringId.getStringData(); + } + String oldText = null; + if(stringData != null) { + oldText = stringData.getString(); + } + if(Objects.equals(text, oldText)){ return; } - Section section = getBlockItem().getSection(SectionType.STRING_DATA); - if(section == null){ + DexIdPool pool = getBlockItem().getPool(SectionType.STRING_DATA); + if(pool == null){ return; } - DexIdPool pool = section.getPool(); - StringData stringData = pool.getOrCreate(text); + if(!overwrite || stringData == null){ + stringData = pool.getOrCreate(text); + } setStringId(stringData.getStringId()); + if(overwrite){ + pool.keyChanged(oldText); + } } @Override diff --git a/src/main/java/com/reandroid/dex/index/TypeId.java b/src/main/java/com/reandroid/dex/index/TypeId.java index 0c84e996b..f93e13ca7 100644 --- a/src/main/java/com/reandroid/dex/index/TypeId.java +++ b/src/main/java/com/reandroid/dex/index/TypeId.java @@ -15,10 +15,7 @@ */ package com.reandroid.dex.index; -import com.reandroid.dex.common.DexUtils; import com.reandroid.dex.item.StringData; -import com.reandroid.dex.pool.DexIdPool; -import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; @@ -42,6 +39,29 @@ public String getKey(){ public void setKey(String key){ setName(key); } + public String getName(){ + StringData stringData = getNameData(); + if(stringData != null){ + return stringData.getString(); + } + return null; + } + public void setName(String name){ + getNameReference().setString(name); + clearTypeName(); + } + public StringData getNameData(){ + return getNameReference().getItem(); + } + + public StringReference getNameReference() { + return nameReference; + } + + public void setName(StringData name){ + nameReference.setItem(name); + } + public TypeName getTypeName(){ TypeName typeName = this.typeName; if(typeName != null){ @@ -65,43 +85,6 @@ private void clearTypeName(){ this.typeName = null; } } - public void setName(String name){ - DexIdPool stringPool = getPool(SectionType.STRING_DATA); - StringData stringData = stringPool.getOrCreate(name); - if(stringData == null){ - stringData = getNameData(); - if(stringData != null){ - String old = stringData.getKey(); - stringData.setString(name); - stringPool.keyChanged(old); - clearTypeName(); - return; - } - } - if(stringData == null){ - stringData = stringPool.getOrCreate(name); - } - setName(stringData); - clearTypeName(); - } - public String getName(){ - StringData stringData = getNameData(); - if(stringData != null){ - return stringData.getString(); - } - return null; - } - public StringData getNameData(){ - return getNameReference().getItem(); - } - - public StringReference getNameReference() { - return nameReference; - } - - public void setName(StringData name){ - nameReference.setItem(name); - } @Override public void refresh() { diff --git a/src/main/java/com/reandroid/dex/item/AnnotationElement.java b/src/main/java/com/reandroid/dex/item/AnnotationElement.java index eb23b53e0..7b9db1559 100644 --- a/src/main/java/com/reandroid/dex/item/AnnotationElement.java +++ b/src/main/java/com/reandroid/dex/item/AnnotationElement.java @@ -37,14 +37,14 @@ public String getKey(){ builder.append("()"); return builder.toString(); } - public DexValue getValue(){ - return (DexValue) getChildes()[1]; + public DexValueBlock getValue(){ + return (DexValueBlock) getChildes()[1]; } - public void setValue(DexValue dexValue){ + public void setValue(DexValueBlock dexValue){ addChild(1, dexValue); } public DexValueType getValueType(){ - DexValue value = getValue(); + DexValueBlock value = getValue(); if(value != null){ return value.getValueType(); } diff --git a/src/main/java/com/reandroid/dex/item/AnnotationGroup.java b/src/main/java/com/reandroid/dex/item/AnnotationGroup.java index 05e00ca09..554cae180 100644 --- a/src/main/java/com/reandroid/dex/item/AnnotationGroup.java +++ b/src/main/java/com/reandroid/dex/item/AnnotationGroup.java @@ -18,7 +18,10 @@ import com.reandroid.dex.sections.SectionType; public class AnnotationGroup extends IntegerOffsetSectionList { - public AnnotationGroup(){ + public AnnotationGroup() { super(SectionType.ANNOTATION_SET); } + @Override + void removeNulls() { + } } diff --git a/src/main/java/com/reandroid/dex/item/AnnotationItem.java b/src/main/java/com/reandroid/dex/item/AnnotationItem.java index 9e7f51d64..5c9c6aade 100644 --- a/src/main/java/com/reandroid/dex/item/AnnotationItem.java +++ b/src/main/java/com/reandroid/dex/item/AnnotationItem.java @@ -15,6 +15,7 @@ */ package com.reandroid.dex.item; +import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.BlockArray; import com.reandroid.arsc.item.ByteItem; import com.reandroid.dex.base.*; @@ -23,6 +24,7 @@ import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliFormat; import com.reandroid.dex.writer.SmaliWriter; +import com.reandroid.utils.collection.CollectionUtil; import java.io.IOException; import java.io.StringWriter; @@ -63,18 +65,14 @@ public AnnotationItem(boolean valueEntry) { public AnnotationItem(){ this(false); } + @Override public String getKey(){ - StringBuilder builder = new StringBuilder(); - boolean appendOnce = false; - for (AnnotationElement element : this){ - if(appendOnce){ - builder.append(','); - } - builder.append(element.getKey()); - appendOnce = true; + AnnotationElement first = CollectionUtil.getFirst(iterator()); + if(first != null){ + return first.getKey(); } - return builder.toString(); + return ""; } @Override public Iterator iterator(){ diff --git a/src/main/java/com/reandroid/dex/item/AnnotationsDirectory.java b/src/main/java/com/reandroid/dex/item/AnnotationsDirectory.java index cf6d63f1b..41220840f 100644 --- a/src/main/java/com/reandroid/dex/item/AnnotationsDirectory.java +++ b/src/main/java/com/reandroid/dex/item/AnnotationsDirectory.java @@ -56,6 +56,9 @@ public AnnotationSet getClassAnnotations(){ public void setClassAnnotations(AnnotationSet annotationSet){ header.classAnnotation.setItem(annotationSet); } + public boolean isEmpty(){ + return header.isEmpty(); + } public Iterator getFieldsAnnotation(int index){ return fieldsOffset.getItems(index); @@ -198,6 +201,12 @@ public Header() { this.parameterCount = new IndirectInteger(this, offset += 4); } + public boolean isEmpty(){ + return classAnnotation.get() == 0 + && fieldCount.get() == 0 + && methodCount.get() == 0 + && parameterCount.get() == 0; + } @Override public void onReadBytes(BlockReader reader) throws IOException { super.onReadBytes(reader); diff --git a/src/main/java/com/reandroid/dex/item/DataItemEntry.java b/src/main/java/com/reandroid/dex/item/DataItemEntry.java index 57a359e16..4af94de5d 100644 --- a/src/main/java/com/reandroid/dex/item/DataItemEntry.java +++ b/src/main/java/com/reandroid/dex/item/DataItemEntry.java @@ -15,12 +15,10 @@ */ package com.reandroid.dex.item; +import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.OffsetSupplier; import com.reandroid.arsc.item.IntegerReference; -import com.reandroid.dex.base.NumberIntegerReference; -import com.reandroid.dex.base.OffsetReceiver; -import com.reandroid.dex.base.PositionedItem; -import com.reandroid.dex.base.StringKeyItem; +import com.reandroid.dex.base.*; public class DataItemEntry extends DexContainerItem implements PositionedItem, OffsetSupplier, OffsetReceiver, StringKeyItem { @@ -31,6 +29,16 @@ public DataItemEntry(int childesCount) { super(childesCount); } + public void removeSelf() { + Block parent = getParent(); + if(!(parent instanceof DexItemArray)){ + return; + } + DexItemArray itemArray = (DexItemArray) parent; + setParent(null); + itemArray.remove(this); + setPosition(0); + } @Override public String getKey() { return null; diff --git a/src/main/java/com/reandroid/dex/item/DexContainerItem.java b/src/main/java/com/reandroid/dex/item/DexContainerItem.java index de270df5a..8b256beda 100644 --- a/src/main/java/com/reandroid/dex/item/DexContainerItem.java +++ b/src/main/java/com/reandroid/dex/item/DexContainerItem.java @@ -18,6 +18,7 @@ import com.reandroid.arsc.base.Block; import com.reandroid.arsc.item.IntegerReference; import com.reandroid.dex.base.FixedDexContainer; +import com.reandroid.dex.pool.DexIdPool; import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionList; import com.reandroid.dex.sections.SectionType; @@ -78,4 +79,11 @@ public Section getSection(SectionType sectionType){ } return null; } + public DexIdPool getPool(SectionType sectionType){ + Section section = getSection(sectionType); + if(section != null){ + return section.getPool(); + } + return null; + } } diff --git a/src/main/java/com/reandroid/dex/item/EncodedArray.java b/src/main/java/com/reandroid/dex/item/EncodedArray.java index a938f1e31..2c6caf72f 100644 --- a/src/main/java/com/reandroid/dex/item/EncodedArray.java +++ b/src/main/java/com/reandroid/dex/item/EncodedArray.java @@ -18,15 +18,15 @@ import com.reandroid.arsc.container.BlockList; import com.reandroid.arsc.io.BlockReader; import com.reandroid.dex.base.Ule128Item; -import com.reandroid.dex.value.DexValue; +import com.reandroid.dex.value.DexValueBlock; import com.reandroid.dex.value.DexValueType; import java.io.IOException; import java.util.Iterator; -public class EncodedArray extends DataItemEntry implements Iterable> { +public class EncodedArray extends DataItemEntry implements Iterable> { private final Ule128Item elementCount; - private final BlockList> elements; + private final BlockList> elements; public EncodedArray() { super(2); this.elementCount = new Ule128Item(); @@ -34,18 +34,18 @@ public EncodedArray() { addChild(0, elementCount); addChild(1, elements); } - public DexValue get(int i){ + public DexValueBlock get(int i){ return getElements().get(i); } public int size(){ return getElements().size(); } @Override - public Iterator> iterator(){ + public Iterator> iterator(){ return getElements().iterator(); } - public BlockList> getElements() { + public BlockList> getElements() { return elements; } @Override @@ -53,7 +53,7 @@ public void onReadBytes(BlockReader reader) throws IOException{ super.onReadBytes(reader); int count = elementCount.get(); for(int i = 0; i < count; i++){ - DexValue dexValue = DexValueType.create(reader); + DexValueBlock dexValue = DexValueType.create(reader); elements.add(dexValue); dexValue.readBytes(reader); } @@ -62,7 +62,7 @@ public void onReadBytes(BlockReader reader) throws IOException{ public String toString(){ StringBuilder builder = new StringBuilder(); builder.append('{'); - Iterator> iterator = iterator(); + Iterator> iterator = iterator(); boolean appendOnce = false; while (iterator.hasNext()){ if(appendOnce){ diff --git a/src/main/java/com/reandroid/dex/item/FieldDef.java b/src/main/java/com/reandroid/dex/item/FieldDef.java index 65c0f0e26..c4c5ad8bc 100644 --- a/src/main/java/com/reandroid/dex/item/FieldDef.java +++ b/src/main/java/com/reandroid/dex/item/FieldDef.java @@ -18,7 +18,7 @@ import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.index.FieldId; import com.reandroid.dex.sections.SectionType; -import com.reandroid.dex.value.DexValue; +import com.reandroid.dex.value.DexValueBlock; import com.reandroid.dex.value.DexValueType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; @@ -31,6 +31,13 @@ public class FieldDef extends Def implements Comparable{ public FieldDef() { super(0, SectionType.FIELD_ID); } + + public DexValueBlock getStaticInitialValue(){ + if(isStatic()){ + return getClassId().getStaticValue(getIndex()); + } + return null; + } public FieldId getFieldId(){ return getItem(); } @@ -56,7 +63,7 @@ public void append(SmaliWriter writer) throws IOException { writer.append(':'); fieldId.getFieldType().append(writer); if(isStatic()){ - DexValue value = getClassId().getStaticValue(getIndex()); + DexValueBlock value = getClassId().getStaticValue(getIndex()); if(value != null && value.getValueType() != DexValueType.NULL){ writer.append(" = "); value.append(writer); diff --git a/src/main/java/com/reandroid/dex/item/IntegerOffsetSectionList.java b/src/main/java/com/reandroid/dex/item/IntegerOffsetSectionList.java index b697c2374..a64c364fb 100644 --- a/src/main/java/com/reandroid/dex/item/IntegerOffsetSectionList.java +++ b/src/main/java/com/reandroid/dex/item/IntegerOffsetSectionList.java @@ -19,6 +19,7 @@ import com.reandroid.utils.collection.ArrayIterator; import java.util.Iterator; +import java.util.function.Predicate; public class IntegerOffsetSectionList extends IntegerList implements Iterable{ private final SectionType sectionType; @@ -29,6 +30,64 @@ public IntegerOffsetSectionList(SectionType sectionType) { this.sectionType = sectionType; } + @Override + public void removeSelf() { + setItems(null); + super.removeSelf(); + } + + public void remove(T item) { + remove(t -> t == item); + } + public void remove(Predicate filter) { + T[] items = this.items; + if(items == null){ + return; + } + int length = items.length; + boolean found = false; + for(int i = 0; i < length; i++){ + T item = items[i]; + if(filter.test(item)){ + items[i] = null; + if(item != null){ + item.removeSelf(); + } + found = true; + } + } + if(found){ + removeNulls(); + } + } + void removeNulls() { + T[] items = this.items; + if(items == null || items.length == 0){ + setItems(null); + return; + } + int length = items.length; + int count = 0; + for(int i = 0; i < length; i++){ + if(items[i] == null){ + count ++; + } + } + if(count == 0){ + return; + } + T[] update = sectionType.getCreator() + .newInstance(length - count); + int index = 0; + for(int i = 0; i < length; i++){ + T element = items[i]; + if(element != null){ + update[index] = element; + index++; + } + } + setItems(update); + } @Override public Iterator iterator() { return ArrayIterator.of(items); @@ -46,6 +105,23 @@ public T getItem(int i){ public T[] getItems() { return items; } + public void setItems(T[] items){ + if(items == this.items){ + return; + } + if(isEmpty(items)){ + this.items = null; + setSize(0); + return; + } + int length = items.length; + setSize(length, false); + for(int i = 0; i < length; i++){ + T item = items[i]; + put(i, getData(item)); + } + this.items = items; + } @Override void onChanged() { @@ -68,9 +144,18 @@ private void refreshItems(){ } int length = items.length; setSize(length, false); + boolean found = false; for(int i = 0; i < length; i++){ T item = items[i]; + int data = getData(item); put(i, getData(item)); + if(data == 0) { + items[i] = null; + found = true; + } + } + if(found){ + removeNulls(); } } private int getData(T item){ diff --git a/src/main/java/com/reandroid/dex/item/StringData.java b/src/main/java/com/reandroid/dex/item/StringData.java index e69cbcc46..869a49dd5 100644 --- a/src/main/java/com/reandroid/dex/item/StringData.java +++ b/src/main/java/com/reandroid/dex/item/StringData.java @@ -20,6 +20,7 @@ import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.IntegerReference; import com.reandroid.dex.base.*; +import com.reandroid.dex.common.DexUtils; import com.reandroid.dex.index.StringId; import com.reandroid.dex.io.ByteReader; import com.reandroid.dex.io.StreamUtil; @@ -197,9 +198,7 @@ public void refresh() { @Override public void append(SmaliWriter writer) throws IOException { - writer.append('"'); - writer.append(getString()); - writer.append('"'); + DexUtils.appendQuotedString(writer, getString()); } @Override public int compareTo(StringData stringData) { diff --git a/src/main/java/com/reandroid/dex/item/TypeList.java b/src/main/java/com/reandroid/dex/item/TypeList.java index 24da781ac..bafd3dde8 100644 --- a/src/main/java/com/reandroid/dex/item/TypeList.java +++ b/src/main/java/com/reandroid/dex/item/TypeList.java @@ -18,12 +18,14 @@ import com.reandroid.dex.base.DexItemArray; import com.reandroid.dex.base.DexPositionAlign; import com.reandroid.dex.index.TypeId; +import com.reandroid.dex.pool.DexIdPool; import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliFormat; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.collection.ArrayIterator; +import com.reandroid.utils.collection.ComputeIterator; import java.io.IOException; import java.util.Iterator; @@ -34,11 +36,43 @@ public class TypeList extends ShortList implements SmaliFormat, Iterable public TypeList() { super(); } - public boolean add(TypeId typeId){ + + public void addAll(Iterator iterator) { + if(!iterator.hasNext()) { + return; + } + DexIdPool pool = getPool(SectionType.TYPE_ID); + if(pool == null) { + return; + } + while (iterator.hasNext()){ + TypeId typeId = pool.getOrCreate(iterator.next()); + add(typeId); + } + } + public void add(String typeName) { + if(typeName == null){ + return; + } + DexIdPool pool = getPool(SectionType.TYPE_ID); + if(pool != null){ + add(pool.getOrCreate(typeName)); + } + } + public void add(TypeId typeId){ + if(typeId != null) { + add(typeId.getIndex()); + }else { + add(0); + } + } + public void remove(TypeId typeId){ if(typeId != null){ - return addIfAbsent(typeId.getIndex()); + remove(indexOf(typeId.getIndex())); } - return false; + } + public Iterator getTypeNames() { + return ComputeIterator.of(iterator(), TypeId::getName); } @Override public Iterator iterator() { @@ -51,6 +85,13 @@ public int size() { public TypeId[] getTypeIds(){ return typeIds; } + public TypeId getTypeId(int index){ + TypeId[] typeIds = getTypeIds(); + if(typeIds != null && index >= 0 && index < typeIds.length){ + return typeIds[index]; + } + return null; + } @Override void onChanged(){ updateTypeIds(); diff --git a/src/main/java/com/reandroid/dex/model/DexClass.java b/src/main/java/com/reandroid/dex/model/DexClass.java index caf4917f1..dde3656e6 100644 --- a/src/main/java/com/reandroid/dex/model/DexClass.java +++ b/src/main/java/com/reandroid/dex/model/DexClass.java @@ -16,43 +16,58 @@ package com.reandroid.dex.model; import com.reandroid.dex.index.ClassId; +import com.reandroid.dex.index.ItemOffsetReference; import com.reandroid.dex.item.StringData; import com.reandroid.dex.index.TypeId; import com.reandroid.dex.item.*; import com.reandroid.dex.writer.SmaliWriter; +import com.reandroid.utils.collection.ComputeIterator; +import com.reandroid.utils.collection.EmptyIterator; import com.reandroid.utils.collection.EmptyList; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.function.Predicate; public class DexClass implements Comparable{ private final ClassId classId; - private List staticFields; - private List instanceFields; - - private List directMethods; - private List virtualMethods; - public DexClass(ClassId classId){ this.classId = classId; + } - this.staticFields = EmptyList.of(); - this.instanceFields = EmptyList.of(); - - this.directMethods = EmptyList.of(); - this.virtualMethods = EmptyList.of(); + public Iterator getStaticFields() { + ClassData classData = getClassData(); + if(classData != null){ + return ComputeIterator.of(classData + .getStaticFields().iterator(), this::createField); + } + return EmptyIterator.of(); + } + public Iterator getInstanceFields() { + return ComputeIterator.of(getClassData() + .getInstanceFields().iterator(), this::createField); + } + public Iterator getDirectMethods() { + return ComputeIterator.of(getClassData() + .getDirectMethods().iterator(), this::createMethod); + } + public Iterator getVirtualMethods() { + return ComputeIterator.of(getClassData() + .getVirtualMethods().iterator(), this::createMethod); } - public List getStaticFields() { - return staticFields; + DexField createField(FieldDef fieldDef){ + fieldDef.setClassId(getClassId()); + return new DexField(this, fieldDef); } - public List getInstanceFields() { - return instanceFields; + DexMethod createMethod(MethodDef methodDef){ + return new DexMethod(this, methodDef); } @@ -85,6 +100,9 @@ public String getName(){ public String getSuperClass(){ return getClassId().getSuperClass().getName(); } + public void setSuperClass(String superClass){ + getClassId().setSuperClass(superClass); + } public String getSourceFile(){ StringData stringData = getClassId().getSourceFile(); if(stringData != null){ @@ -92,10 +110,50 @@ public String getSourceFile(){ } return null; } - public TypeId[] getInterfaces(){ - return getClassId().getInterfaceTypeIds(); + public void setSourceFile(String sourceFile){ + getClassId().setSourceFile(sourceFile); + } + public Iterator getInterfaces(){ + TypeList typeList = getClassId().getInterfaces(); + if(typeList != null){ + return typeList.getTypeNames(); + } + return EmptyIterator.of(); + } + public void addInterface(String typeName) { + ItemOffsetReference reference = getClassId().getInterfacesReference(); + TypeList typeList = reference.getOrCreate(); + typeList.add(typeName); + } + public void addInterfaces(Iterator iterator){ + ItemOffsetReference reference = getClassId().getInterfacesReference(); + TypeList typeList = reference.getOrCreate(); + typeList.addAll(iterator); + } + public void clearInterfaces() { + ItemOffsetReference reference = getClassId().getInterfacesReference(); + reference.setItem(null); } + public void removeAnnotations(Predicate filter) { + ClassId classId = getClassId(); + AnnotationSet annotationSet = classId.getClassAnnotations(); + if(annotationSet == null) { + return; + } + annotationSet.remove(filter); + annotationSet.refresh(); + if(annotationSet.size() == 0){ + annotationSet.removeSelf(); + classId.setClassAnnotations(null); + AnnotationsDirectory directory = getClassId().getAnnotationsDirectory(); + if(directory != null && directory.isEmpty()){ + directory.removeSelf(); + classId.setAnnotationsDirectory(null); + } + } + getClassId().refresh(); + } public AnnotationSet getAnnotations(){ return getClassId().getClassAnnotations(); } @@ -113,24 +171,6 @@ AnnotationsDirectory getAnnotationsDirectory(){ public void refresh(){ - loadFields(); - loadMethods(); - } - private void loadFields(){ - ClassData classData = getClassData(); - if(classData == null){ - return; - } - this.staticFields = DexField.create(this, classData.getStaticFields()); - this.instanceFields = DexField.create(this, classData.getInstanceFields()); - } - private void loadMethods(){ - ClassData classData = getClassData(); - if(classData == null){ - return; - } - this.directMethods = DexMethod.create(this, classData.getDirectMethods()); - this.virtualMethods = DexMethod.create(this, classData.getVirtualMethods()); } @Override diff --git a/src/main/java/com/reandroid/dex/model/DexField.java b/src/main/java/com/reandroid/dex/model/DexField.java index b88604ce0..1dd83590b 100644 --- a/src/main/java/com/reandroid/dex/model/DexField.java +++ b/src/main/java/com/reandroid/dex/model/DexField.java @@ -15,16 +15,15 @@ */ package com.reandroid.dex.model; +import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.index.FieldId; -import com.reandroid.dex.item.StringData; +import com.reandroid.dex.index.TypeId; import com.reandroid.dex.item.AnnotationSet; import com.reandroid.dex.item.FieldDef; -import com.reandroid.dex.item.FieldDefArray; -import com.reandroid.utils.collection.EmptyList; +import com.reandroid.dex.item.StringData; +import com.reandroid.dex.value.DexValueBlock; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; public class DexField { @@ -36,18 +35,29 @@ public DexField(DexClass dexClass, FieldDef fieldDef){ this.fieldDef = fieldDef; } + public String getAccessFlags() { + return AccessFlag.formatForField(getFieldDef().getAccessFlagsValue()); + } public String getName(){ return getFieldId().getName(); } public void setName(String name){ getFieldId().setName(name); } - public void setName(StringData name){ - getFieldId().setName(name); + public String getFieldType(){ + return getFieldId().getFieldTypeName(); + } + public void setFieldType(String type){ + getFieldId().setFieldType(type); } - public String key(){ - return getFieldId().key(); + public String getInitialValue() { + DexValueBlock dexValueBlock = getFieldDef().getStaticInitialValue(); + if(dexValueBlock != null){ + return dexValueBlock.getAsString(); + } + return null; } + public String getKey(){ return getFieldId().getKey(); } @@ -67,23 +77,22 @@ public Iterator getAnnotations(){ @Override public String toString() { - return key(); - } - - static DexField create(DexClass dexClass, FieldDef fieldDef){ - return new DexField(dexClass, fieldDef); - } - static List create(DexClass dexClass, FieldDefArray defArray){ - int count = defArray.getChildesCount(); - if(count == 0){ - return EmptyList.of(); + StringBuilder builder = new StringBuilder(); + builder.append(".field"); + String flags = getAccessFlags(); + if(flags.length() != 0){ + builder.append(' '); + builder.append(flags); } - List results = new ArrayList<>(count); - Iterator iterator = defArray.iterator(); - while (iterator.hasNext()){ - results.add(create(dexClass, iterator.next())); + builder.append(' '); + builder.append(getName()); + builder.append(":"); + builder.append(getFieldType()); + String value = getInitialValue(); + if(value != null){ + builder.append(" = "); + builder.append(value); } - return results; + return builder.toString(); } - } diff --git a/src/main/java/com/reandroid/dex/model/DexFile.java b/src/main/java/com/reandroid/dex/model/DexFile.java index 51f2ae445..35fb4b036 100644 --- a/src/main/java/com/reandroid/dex/model/DexFile.java +++ b/src/main/java/com/reandroid/dex/model/DexFile.java @@ -16,18 +16,13 @@ package com.reandroid.dex.model; import com.reandroid.arsc.base.Block; -import com.reandroid.arsc.container.FixedBlockContainer; -import com.reandroid.arsc.group.ItemGroup; import com.reandroid.arsc.io.BlockReader; -import com.reandroid.common.BytesOutputStream; -import com.reandroid.dex.header.DexHeader; -import com.reandroid.dex.item.AnnotationElement; -import com.reandroid.dex.item.AnnotationItem; +import com.reandroid.dex.index.ClassId; import com.reandroid.dex.item.StringData; -import com.reandroid.dex.sections.*; -import com.reandroid.dex.value.ArrayValue; -import com.reandroid.dex.value.DexValue; -import com.reandroid.dex.value.StringValue; +import com.reandroid.dex.sections.DexFileBlock; +import com.reandroid.dex.sections.Marker; +import com.reandroid.dex.sections.Section; +import com.reandroid.dex.sections.SectionType; import com.reandroid.utils.collection.CollectionUtil; import com.reandroid.utils.collection.ComputeIterator; import com.reandroid.utils.collection.EmptyIterator; @@ -37,40 +32,15 @@ import java.util.Iterator; import java.util.List; -public class DexFile extends FixedBlockContainer { +public class DexFile { - private final SectionList sectionList; + private final DexFileBlock dexFileBlock; - public DexFile() { - super(1); - this.sectionList = new SectionList(); - addChild(0, sectionList); + public DexFile(DexFileBlock dexFileBlock){ + this.dexFileBlock = dexFileBlock; } - - public Iterator unusedStrings(){ - return getStringsWithUsage(StringData.USAGE_NONE); - } - public Iterator getStringsContainsUsage(int usage){ - return FilterIterator.of(getStrings(), - stringData -> stringData.containsUsage(usage)); - } - public Iterator getStringsWithUsage(int usage){ - return FilterIterator.of(getStrings(), - stringData -> stringData.getStringUsage() == usage); - } - public Iterator getStrings(){ - Section stringSection = get(SectionType.STRING_DATA); - if(stringSection == null){ - return EmptyIterator.of(); - } - return stringSection.iterator(); - } - public void clearMarkers(){ - List removeList = CollectionUtil.toList( - ComputeIterator.of(getMarkers(), Marker::getStringData)); - for(StringData stringData : removeList){ - stringData.removeSelf(); - } + public Iterator getDexClasses() { + return ComputeIterator.of(getClassIds(), DexClass::new); } public Marker getOrCreateMarker() { Marker marker = CollectionUtil.getFirst(getMarkers()); @@ -89,95 +59,57 @@ public Marker getOrCreateMarker() { public Iterator getMarkers() { return Marker.parse(this); } + public void clearMarkers(){ + List removeList = CollectionUtil.toList( + ComputeIterator.of(getMarkers(), Marker::getStringData)); + for(StringData stringData : removeList){ + stringData.removeSelf(); + } + } public void sortSection(SectionType[] order){ refresh(); - getSectionList().sortSection(order); + getDexFileBlock().sortSection(order); refresh(); } public void sortStrings(){ - getSectionList().sortStrings(); - } - public void linkTypeSignature(){ - Section annotationSection = getSectionList().get(SectionType.ANNOTATION); - if(annotationSection == null){ - return; - } - ItemGroup group = annotationSection.getPool().getGroup(ANNOTATION_SIG_KEY); - if(group == null){ - return; - } - for(AnnotationItem item : group){ - AnnotationElement element = item.getElement(0); - if(element == null){ - continue; - } - DexValue dexValue = element.getValue(); - if(!(dexValue instanceof ArrayValue)){ - continue; - } - ArrayValue arrayValue = (ArrayValue) dexValue; - linkTypeSignature(arrayValue); - } - } - private void linkTypeSignature(ArrayValue arrayValue){ - for(DexValue value : arrayValue){ - if(!(value instanceof StringValue)){ - continue; - } - StringData stringData = ((StringValue) value).getStringData(); - if(stringData != null){ - stringData.addStringUsage(StringData.USAGE_TYPE); - } - } + getDexFileBlock().sortStrings(); } - - public Section get(SectionType sectionType){ - return getSectionList().get(sectionType); - } - public DexHeader getHeader() { - return getSectionList().getHeader(); - } - public SectionList getSectionList(){ - return sectionList; + public Iterator unusedStrings(){ + return getStringsWithUsage(StringData.USAGE_NONE); } - public MapList getMapList(){ - return getSectionList().getMapList(); + public Iterator getStringsContainsUsage(int usage){ + return FilterIterator.of(getStringData(), + stringData -> stringData.containsUsage(usage)); } - - @Override - protected void onPreRefresh() { - sectionList.refresh(); + public Iterator getStringsWithUsage(int usage){ + return FilterIterator.of(getStringData(), + stringData -> stringData.getStringUsage() == usage); } - @Override - protected void onRefreshed() { - sectionList.updateHeader(); + Iterator getClassIds(){ + Section section = get(SectionType.CLASS_ID); + if(section != null){ + return section.iterator(); + } + return EmptyIterator.of(); } - @Override - public byte[] getBytes(){ - BytesOutputStream outputStream = new BytesOutputStream( - getHeader().fileSize.get()); - try { - writeBytes(outputStream); - outputStream.close(); - } catch (IOException ignored) { + public Iterator getStringData(){ + Section section = get(SectionType.STRING_DATA); + if(section != null){ + return section.iterator(); } - return outputStream.toByteArray(); + return EmptyIterator.of(); } - - public void read(byte[] dexBytes) throws IOException { - BlockReader reader = new BlockReader(dexBytes); - readBytes(reader); - reader.close(); + public Section get(SectionType sectionType){ + return getDexFileBlock().get(sectionType); } - public void read(InputStream inputStream) throws IOException { - BlockReader reader = new BlockReader(inputStream); - readBytes(reader); - reader.close(); + public void refresh() { + getDexFileBlock().refresh(); } - public void read(File file) throws IOException { - BlockReader reader = new BlockReader(file); - readBytes(reader); - reader.close(); + public DexFileBlock getDexFileBlock() { + return dexFileBlock; + } + public byte[] getBytes() { + return getDexFileBlock().getBytes(); } public void write(File file) throws IOException { File dir = file.getParentFile(); @@ -185,41 +117,32 @@ public void write(File file) throws IOException { dir.mkdirs(); } FileOutputStream outputStream = new FileOutputStream(file); - writeBytes(outputStream); + write(outputStream); outputStream.close(); } - public static boolean isDexFile(File file){ - if(file == null || !file.isFile()){ - return false; - } - DexHeader dexHeader = null; - try { - InputStream inputStream = new FileInputStream(file); - dexHeader = DexHeader.readHeader(inputStream); - inputStream.close(); - } catch (IOException ignored) { - } - return isDexFile(dexHeader); - } - public static boolean isDexFile(InputStream inputStream){ - DexHeader dexHeader = null; - try { - dexHeader = DexHeader.readHeader(inputStream); - inputStream.close(); - } catch (IOException ignored) { - } - return isDexFile(dexHeader); + public void write(OutputStream outputStream) throws IOException { + byte[] bytes = getBytes(); + outputStream.write(bytes, 0, bytes.length); } - private static boolean isDexFile(DexHeader dexHeader){ - if(dexHeader == null){ - return false; - } - if(dexHeader.magic.isDefault()){ - return false; - } - int version = dexHeader.version.getVersionAsInteger(); - return version > 0 && version < 1000; + + @Override + public String toString() { + return getDexFileBlock().getMapList().toString(); } - public static final String ANNOTATION_SIG_KEY = "Ldalvik/annotation/Signature;->value()"; + public static DexFile read(byte[] dexBytes) throws IOException { + return read(new BlockReader(dexBytes)); + } + public static DexFile read(InputStream inputStream) throws IOException { + return read(new BlockReader(inputStream)); + } + public static DexFile read(File file) throws IOException { + return read(new BlockReader(file)); + } + public static DexFile read(BlockReader reader) throws IOException { + DexFileBlock dexFileBlock = new DexFileBlock(); + dexFileBlock.readBytes(reader); + reader.close(); + return new DexFile(dexFileBlock); + } } diff --git a/src/main/java/com/reandroid/dex/model/DexMethod.java b/src/main/java/com/reandroid/dex/model/DexMethod.java index 6f65cd0b7..349ba5019 100644 --- a/src/main/java/com/reandroid/dex/model/DexMethod.java +++ b/src/main/java/com/reandroid/dex/model/DexMethod.java @@ -15,14 +15,13 @@ */ package com.reandroid.dex.model; +import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.index.MethodId; +import com.reandroid.dex.index.TypeId; import com.reandroid.dex.item.MethodDef; -import com.reandroid.dex.item.MethodDefArray; -import com.reandroid.utils.collection.EmptyList; +import com.reandroid.utils.collection.ComputeIterator; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; public class DexMethod { private final DexClass dexClass; @@ -33,12 +32,36 @@ public DexMethod(DexClass dexClass, MethodDef methodDef){ this.methodDef = methodDef; } + public String getAccessFlags() { + return AccessFlag.formatForField(getMethodDef().getAccessFlagsValue()); + } public String getName(){ return getMethodId().getName(); } - public String key(){ - return getMethodId().key(); + public void setName(String name){ + getMethodId().setName(name); + } + public int getParametersCount() { + return getMethodId().getParametersCount(); + } + public String getParameter(int index) { + TypeId typeId = getMethodId().getParameter(index); + if(typeId != null){ + return typeId.getName(); + } + return null; + } + public Iterator getParameters() { + return ComputeIterator.of(getMethodId().getParameters(), TypeId::getName); + } + public String getReturnType() { + TypeId typeId = getMethodId().getReturnTypeId(); + if(typeId != null) { + return typeId.getName(); + } + return null; } + public String getKey(){ return getMethodId().getKey(); } @@ -53,22 +76,6 @@ public MethodDef getMethodDef() { } @Override public String toString() { - return key(); - } - - static DexMethod create(DexClass dexClass, MethodDef fieldDef){ - return new DexMethod(dexClass, fieldDef); - } - static List create(DexClass dexClass, MethodDefArray defArray){ - int count = defArray.getChildesCount(); - if(count == 0){ - return EmptyList.of(); - } - List results = new ArrayList<>(count); - Iterator iterator = defArray.iterator(); - while (iterator.hasNext()){ - results.add(create(dexClass, iterator.next())); - } - return results; + return getKey(); } } diff --git a/src/main/java/com/reandroid/dex/pool/DexIdPool.java b/src/main/java/com/reandroid/dex/pool/DexIdPool.java index f821aa73c..6db2c089a 100644 --- a/src/main/java/com/reandroid/dex/pool/DexIdPool.java +++ b/src/main/java/com/reandroid/dex/pool/DexIdPool.java @@ -39,7 +39,7 @@ public DexIdPool(Section section){ } public void keyChanged(String old){ - if(!isKeyItems()){ + if(old == null || !isKeyItems()){ return; } ItemGroup exist = itemsMap.remove(old); diff --git a/src/main/java/com/reandroid/dex/refactor/Rename.java b/src/main/java/com/reandroid/dex/refactor/Rename.java index 5d9abe29b..38175f975 100644 --- a/src/main/java/com/reandroid/dex/refactor/Rename.java +++ b/src/main/java/com/reandroid/dex/refactor/Rename.java @@ -16,7 +16,7 @@ package com.reandroid.dex.refactor; import com.reandroid.dex.item.StringData; -import com.reandroid.dex.model.DexFile; +import com.reandroid.dex.sections.DexFileBlock; import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionList; import com.reandroid.dex.sections.SectionType; @@ -39,9 +39,9 @@ public Rename(){ this.renameInfoList = new ArrayList<>(); } - public void apply(DexFile dexFile){ - dexFile.linkTypeSignature(); - SectionList sectionList = dexFile.getSectionList(); + public void apply(DexFileBlock dexFileBlock){ + dexFileBlock.linkTypeSignature(); + SectionList sectionList = dexFileBlock.getSectionList(); for(RenameInfo renameInfo : this){ renameInfo.apply(sectionList); } diff --git a/src/main/java/com/reandroid/dex/sections/DexFileBlock.java b/src/main/java/com/reandroid/dex/sections/DexFileBlock.java new file mode 100644 index 000000000..bd140b611 --- /dev/null +++ b/src/main/java/com/reandroid/dex/sections/DexFileBlock.java @@ -0,0 +1,197 @@ +/* + * 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.sections; + +import com.reandroid.arsc.base.Block; +import com.reandroid.arsc.container.FixedBlockContainer; +import com.reandroid.arsc.group.ItemGroup; +import com.reandroid.arsc.io.BlockReader; +import com.reandroid.common.BytesOutputStream; +import com.reandroid.dex.header.DexHeader; +import com.reandroid.dex.item.AnnotationElement; +import com.reandroid.dex.item.AnnotationItem; +import com.reandroid.dex.item.StringData; +import com.reandroid.dex.value.ArrayValue; +import com.reandroid.dex.value.DexValueBlock; +import com.reandroid.dex.value.StringValue; +import com.reandroid.utils.collection.EmptyIterator; +import com.reandroid.utils.collection.FilterIterator; + +import java.io.*; +import java.util.Iterator; + +public class DexFileBlock extends FixedBlockContainer { + + private final SectionList sectionList; + + public DexFileBlock() { + super(1); + this.sectionList = new SectionList(); + addChild(0, sectionList); + } + + public Iterator unusedStrings(){ + return getStringsWithUsage(StringData.USAGE_NONE); + } + public Iterator getStringsContainsUsage(int usage){ + return FilterIterator.of(getStrings(), + stringData -> stringData.containsUsage(usage)); + } + public Iterator getStringsWithUsage(int usage){ + return FilterIterator.of(getStrings(), + stringData -> stringData.getStringUsage() == usage); + } + public Iterator getStrings(){ + Section stringSection = get(SectionType.STRING_DATA); + if(stringSection == null){ + return EmptyIterator.of(); + } + return stringSection.iterator(); + } + public void sortSection(SectionType[] order){ + refresh(); + getSectionList().sortSection(order); + refresh(); + } + public void sortStrings(){ + getSectionList().sortStrings(); + } + public void linkTypeSignature(){ + Section annotationSection = getSectionList().get(SectionType.ANNOTATION); + if(annotationSection == null){ + return; + } + ItemGroup group = annotationSection.getPool().getGroup(ANNOTATION_SIG_KEY); + if(group == null){ + return; + } + for(AnnotationItem item : group){ + AnnotationElement element = item.getElement(0); + if(element == null){ + continue; + } + DexValueBlock dexValue = element.getValue(); + if(!(dexValue instanceof ArrayValue)){ + continue; + } + ArrayValue arrayValue = (ArrayValue) dexValue; + linkTypeSignature(arrayValue); + } + } + private void linkTypeSignature(ArrayValue arrayValue){ + for(DexValueBlock value : arrayValue){ + if(!(value instanceof StringValue)){ + continue; + } + StringData stringData = ((StringValue) value).getStringData(); + if(stringData != null){ + stringData.addStringUsage(StringData.USAGE_TYPE); + } + } + } + + public Section get(SectionType sectionType){ + return getSectionList().get(sectionType); + } + public DexHeader getHeader() { + return getSectionList().getHeader(); + } + public SectionList getSectionList(){ + return sectionList; + } + public MapList getMapList(){ + return getSectionList().getMapList(); + } + + @Override + protected void onPreRefresh() { + sectionList.refresh(); + } + @Override + protected void onRefreshed() { + sectionList.updateHeader(); + } + @Override + public byte[] getBytes(){ + BytesOutputStream outputStream = new BytesOutputStream( + getHeader().fileSize.get()); + try { + writeBytes(outputStream); + outputStream.close(); + } catch (IOException ignored) { + } + return outputStream.toByteArray(); + } + + public void read(byte[] dexBytes) throws IOException { + BlockReader reader = new BlockReader(dexBytes); + readBytes(reader); + reader.close(); + } + public void read(InputStream inputStream) throws IOException { + BlockReader reader = new BlockReader(inputStream); + readBytes(reader); + reader.close(); + } + public void read(File file) throws IOException { + BlockReader reader = new BlockReader(file); + readBytes(reader); + reader.close(); + } + public void write(File file) throws IOException { + File dir = file.getParentFile(); + if(dir != null && !dir.exists()){ + dir.mkdirs(); + } + FileOutputStream outputStream = new FileOutputStream(file); + writeBytes(outputStream); + outputStream.close(); + } + public static boolean isDexFile(File file){ + if(file == null || !file.isFile()){ + return false; + } + DexHeader dexHeader = null; + try { + InputStream inputStream = new FileInputStream(file); + dexHeader = DexHeader.readHeader(inputStream); + inputStream.close(); + } catch (IOException ignored) { + } + return isDexFile(dexHeader); + } + public static boolean isDexFile(InputStream inputStream){ + DexHeader dexHeader = null; + try { + dexHeader = DexHeader.readHeader(inputStream); + inputStream.close(); + } catch (IOException ignored) { + } + return isDexFile(dexHeader); + } + private static boolean isDexFile(DexHeader dexHeader){ + if(dexHeader == null){ + return false; + } + if(dexHeader.magic.isDefault()){ + return false; + } + int version = dexHeader.version.getVersionAsInteger(); + return version > 0 && version < 1000; + } + + public static final String ANNOTATION_SIG_KEY = "Ldalvik/annotation/Signature;->value()"; +} diff --git a/src/main/java/com/reandroid/dex/sections/MapItem.java b/src/main/java/com/reandroid/dex/sections/MapItem.java index 5ee9e18d3..d400fc842 100644 --- a/src/main/java/com/reandroid/dex/sections/MapItem.java +++ b/src/main/java/com/reandroid/dex/sections/MapItem.java @@ -17,7 +17,6 @@ import com.reandroid.arsc.base.Block; import com.reandroid.arsc.item.IntegerReference; -import com.reandroid.dex.model.DexFile; import com.reandroid.dex.base.*; import com.reandroid.utils.HexUtil; @@ -53,7 +52,7 @@ public Section createNewSection(){ parent = getParentInstance(MapList.class); } if(parent == null){ - parent = getParentInstance(DexFile.class); + parent = getParentInstance(DexFileBlock.class); } if(parent == null){ parent = getParent(); diff --git a/src/main/java/com/reandroid/dex/sections/Marker.java b/src/main/java/com/reandroid/dex/sections/Marker.java index e03f050d4..3d252564e 100644 --- a/src/main/java/com/reandroid/dex/sections/Marker.java +++ b/src/main/java/com/reandroid/dex/sections/Marker.java @@ -221,8 +221,7 @@ public int hashCode() { } public static Iterator parse(DexFile dexFile){ - Section stringSection = dexFile.getSectionList().get(SectionType.STRING_DATA); - return ComputeIterator.of(stringSection.iterator(), Marker::parse); + return ComputeIterator.of(dexFile.getStringData(), Marker::parse); } public static Marker parse(StringData stringData) { String str = stringData.getString(); @@ -259,6 +258,9 @@ public static Marker parse(String dexString) { public static Marker createR8() { return parse(R8_TEMPLATE); } + public static Marker createD8() { + return parse(D8_TEMPLATE); + } public static boolean hasMarkerPrefix(String content) { if(content == null || content.length() < 3){ return false; @@ -291,5 +293,6 @@ public enum Backend { } private static final String R8_TEMPLATE = "~~R8{\"backend\":\"dex\",\"compilation-mode\":\"release\",\"has-checksums\":false,\"r8-mode\":\"compatibility\",\"version\":\"3.2.74\"}"; + private static final String D8_TEMPLATE = "~~D8{\"backend\":\"dex\",\"compilation-mode\":\"release\",\"has-checksums\":false,\"min-api\":24,\"version\":\"4.0.48\"}"; } diff --git a/src/main/java/com/reandroid/dex/sections/Section.java b/src/main/java/com/reandroid/dex/sections/Section.java index 953eb5b3d..9a0b5dcc1 100644 --- a/src/main/java/com/reandroid/dex/sections/Section.java +++ b/src/main/java/com/reandroid/dex/sections/Section.java @@ -121,6 +121,29 @@ public T[] getAt(int[] offsets){ } return results; } + public T createOffsetItem() { + int position = estimateLastOffset(); + T item = getItemArray().createNext(); + if(item instanceof PositionedItem) { + ((PositionedItem) item).setPosition(position); + }else { + IntegerReference supplier = ((OffsetSupplier) item).getOffsetReference(); + supplier.set(position); + } + return item; + } + private int estimateLastOffset() { + int offset; + T last = getItemArray().getLast(); + if(last instanceof OffsetSupplier) { + IntegerReference supplier = ((OffsetSupplier) last).getOffsetReference(); + offset = supplier.get(); + offset += last.countBytes(); + }else { + offset = getOffset() + countBytes(); + } + return offset; + } @Override public int getCount(){ return getItemArray().getCount(); diff --git a/src/main/java/com/reandroid/dex/sections/SectionList.java b/src/main/java/com/reandroid/dex/sections/SectionList.java index db80f49f7..fae05c1e8 100644 --- a/src/main/java/com/reandroid/dex/sections/SectionList.java +++ b/src/main/java/com/reandroid/dex/sections/SectionList.java @@ -76,8 +76,12 @@ public SectionList() { typeMap.put(SectionType.MAP_LIST, mapListSection); } - public void updateHeader(){ - dexHeader.updateHeaderInternal(this); + public void updateHeader() { + Block parent = getParentInstance(DexFileBlock.class); + if(parent == null){ + parent = this; + } + dexHeader.updateHeaderInternal(parent); } @Override diff --git a/src/main/java/com/reandroid/dex/value/ArrayValue.java b/src/main/java/com/reandroid/dex/value/ArrayValue.java index e27b30d73..c9ba35139 100644 --- a/src/main/java/com/reandroid/dex/value/ArrayValue.java +++ b/src/main/java/com/reandroid/dex/value/ArrayValue.java @@ -16,40 +16,39 @@ package com.reandroid.dex.value; import com.reandroid.arsc.container.BlockList; -import com.reandroid.arsc.io.BlockReader; import com.reandroid.dex.item.EncodedArray; import com.reandroid.dex.writer.SmaliWriter; import java.io.IOException; import java.util.Iterator; -public class ArrayValue extends DexValue - implements Iterable> { +public class ArrayValue extends DexValueBlock + implements Iterable> { public ArrayValue() { super(new EncodedArray()); } - public DexValue get(int i){ + public DexValueBlock get(int i){ return getElementBlockList().get(i); } public int size() { return getElementBlockList().size(); } - public void addValue(DexValue value){ + public void addValue(DexValueBlock value){ getElementBlockList().add(value); } - public BlockList> getElementBlockList() { + public BlockList> getElementBlockList() { return getValue().getElements(); } @Override - public Iterator> iterator() { + public Iterator> iterator() { return getElementBlockList().iterator(); } @Override public String getTypeName(){ StringBuilder builder = new StringBuilder(); builder.append('['); - Iterator> iterator = iterator(); + Iterator> iterator = iterator(); if(iterator.hasNext()){ builder.append(iterator.next().getTypeName()); } @@ -59,7 +58,7 @@ public String getTypeName(){ public void append(SmaliWriter writer) throws IOException { writer.append('{'); writer.indentPlus(); - BlockList> elements = getElementBlockList(); + BlockList> elements = getElementBlockList(); int count = elements.size(); for(int i = 0; i < count; i++){ if(i != 0){ diff --git a/src/main/java/com/reandroid/dex/value/BooleanValue.java b/src/main/java/com/reandroid/dex/value/BooleanValue.java index 2d2a31e38..4302f3427 100644 --- a/src/main/java/com/reandroid/dex/value/BooleanValue.java +++ b/src/main/java/com/reandroid/dex/value/BooleanValue.java @@ -20,7 +20,7 @@ import java.io.IOException; -public class BooleanValue extends DexValue { +public class BooleanValue extends DexValueBlock { public BooleanValue(){ super(); } @@ -32,7 +32,11 @@ public void append(SmaliWriter writer) throws IOException { writer.append(Boolean.toString(getBoolean())); } @Override - public String toString(){ + public String getAsString() { return Boolean.toString(getBoolean()); } + @Override + public String toString(){ + return getAsString(); + } } diff --git a/src/main/java/com/reandroid/dex/value/CharValue.java b/src/main/java/com/reandroid/dex/value/CharValue.java index 0a5ef5443..340de5c70 100644 --- a/src/main/java/com/reandroid/dex/value/CharValue.java +++ b/src/main/java/com/reandroid/dex/value/CharValue.java @@ -20,54 +20,22 @@ import java.io.IOException; -public class CharValue extends PrimitiveValue{ +public class CharValue extends PrimitiveValue { + public CharValue(){ super(); } + public char getChar(){ return (char) getNumberValue(); } + @Override public void onReadBytes(BlockReader reader) throws IOException { super.onReadBytes(reader); } - - @Override - public void append(SmaliWriter writer) throws IOException { - char ch = getChar(); - if ((ch >= ' ') && (ch < 0x7f)) { - writer.append('\''); - if ((ch == '\'') || (ch == '\"') || (ch == '\\')) { - writer.append('\\'); - } - writer.append(ch); - writer.append('\''); - return; - } else if (ch <= 0x7f) { - switch (ch) { - case '\n': - writer.append("'\\n'"); - return; - case '\r': - writer.append("'\\r'"); - return; - case '\t': - writer.append("'\\t'"); - return; - } - } - - writer.append('\''); - writer.append("\\u"); - writer.append(Character.forDigit(ch >> 12, 16)); - writer.append(Character.forDigit((ch >> 8) & 0x0f, 16)); - writer.append(Character.forDigit((ch >> 4) & 0x0f, 16)); - writer.append(Character.forDigit(ch & 0x0f, 16)); - writer.append('\''); - } - @Override - public String toString() { + public String getAsString() { StringBuilder builder = new StringBuilder(); char ch = getChar(); if ((ch >= ' ') && (ch < 0x7f)) { @@ -101,4 +69,13 @@ public String toString() { builder.append('\''); return builder.toString(); } + + @Override + public void append(SmaliWriter writer) throws IOException { + writer.append(getAsString()); + } + @Override + public String toString() { + return getAsString(); + } } diff --git a/src/main/java/com/reandroid/dex/value/DexValue.java b/src/main/java/com/reandroid/dex/value/DexValueBlock.java similarity index 88% rename from src/main/java/com/reandroid/dex/value/DexValue.java rename to src/main/java/com/reandroid/dex/value/DexValueBlock.java index 7123b550e..70570eda3 100644 --- a/src/main/java/com/reandroid/dex/value/DexValue.java +++ b/src/main/java/com/reandroid/dex/value/DexValueBlock.java @@ -20,20 +20,21 @@ import com.reandroid.arsc.item.ByteItem; import com.reandroid.dex.writer.SmaliFormat; import com.reandroid.dex.writer.SmaliWriter; +import com.reandroid.utils.HexUtil; import java.io.IOException; -public class DexValue extends FixedBlockContainer implements SmaliFormat { +public class DexValueBlock extends FixedBlockContainer implements SmaliFormat { private final ByteItem valueType; private final T valueContainer; - DexValue(T value){ + DexValueBlock(T value){ super(2); valueType = new ByteItem(); valueContainer = value; addChild(0, valueType); addChild(1, valueContainer); } - DexValue(){ + DexValueBlock(){ this(null); } @@ -64,6 +65,10 @@ public void append(SmaliWriter writer) throws IOException { ((SmaliFormat)value).append(writer); } } + + public String getAsString() { + return String.valueOf(getValue()); + } @Override public String toString() { return String.valueOf(getValue()); diff --git a/src/main/java/com/reandroid/dex/value/DexValueType.java b/src/main/java/com/reandroid/dex/value/DexValueType.java index 5db20e01d..e6616e414 100644 --- a/src/main/java/com/reandroid/dex/value/DexValueType.java +++ b/src/main/java/com/reandroid/dex/value/DexValueType.java @@ -28,7 +28,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; -public class DexValueType> implements BlockCreator { +public class DexValueType> implements BlockCreator { private static final DexValueType[] VALUES; private static final DexValueType[] VALUES_COPY; @@ -48,7 +48,7 @@ public class DexValueType> implements BlockCreator { public static final DexValueType> METHOD; public static final DexValueType ENUM; public static final DexValueType ARRAY; - public static final DexValueType> ANNOTATION; + public static final DexValueType> ANNOTATION; public static final DexValueType NULL; public static final DexValueType BOOLEAN; @@ -118,7 +118,7 @@ public class DexValueType> implements BlockCreator { valueTypes[0x1c] = ARRAY; ANNOTATION = new DexValueType<>("ANNOTATION", 0x1d, 0, false, - "", () -> new DexValue<>(new AnnotationItem(true))); + "", () -> new DexValueBlock<>(new AnnotationItem(true))); valueTypes[0x1d] = ANNOTATION; NULL = new DexValueType<>("NULL", 0x1e, 0, false, @@ -207,7 +207,7 @@ public static DexValueType fromFlag(int flag){ return VALUES[flag & 0x1f]; } - public static DexValue create(BlockReader reader) throws IOException { + public static DexValueBlock create(BlockReader reader) throws IOException { int type = reader.read(); reader.offset(-1); DexValueType valueType = fromFlag(type); diff --git a/src/main/java/com/reandroid/dex/value/NullValue.java b/src/main/java/com/reandroid/dex/value/NullValue.java index 31f4becf7..2f0c6379d 100644 --- a/src/main/java/com/reandroid/dex/value/NullValue.java +++ b/src/main/java/com/reandroid/dex/value/NullValue.java @@ -20,7 +20,7 @@ import java.io.IOException; -public class NullValue extends DexValue { +public class NullValue extends DexValueBlock { public NullValue() { super(); } @@ -34,6 +34,10 @@ public void append(SmaliWriter writer) throws IOException { writer.append("null"); } @Override + public String getAsString() { + return "null"; + } + @Override public String toString() { return "NullValue"; } diff --git a/src/main/java/com/reandroid/dex/value/PrimitiveValue.java b/src/main/java/com/reandroid/dex/value/PrimitiveValue.java index 7702c74b3..b71589052 100644 --- a/src/main/java/com/reandroid/dex/value/PrimitiveValue.java +++ b/src/main/java/com/reandroid/dex/value/PrimitiveValue.java @@ -21,7 +21,7 @@ import java.io.IOException; -public class PrimitiveValue extends DexValue { +public class PrimitiveValue extends DexValueBlock { public PrimitiveValue() { super(new NumberValue()); @@ -42,7 +42,11 @@ public void append(SmaliWriter writer) throws IOException { writer.append(HexUtil.toHex(getNumberValue(), getValueSize())); } @Override - public String toString() { + public String getAsString() { return HexUtil.toHex(getNumberValue(), getValueSize()); } + @Override + public String toString() { + return getAsString(); + } } diff --git a/src/main/java/com/reandroid/dex/value/SectionValue.java b/src/main/java/com/reandroid/dex/value/SectionValue.java index 3007de822..fdf3eff69 100644 --- a/src/main/java/com/reandroid/dex/value/SectionValue.java +++ b/src/main/java/com/reandroid/dex/value/SectionValue.java @@ -17,6 +17,7 @@ import com.reandroid.arsc.base.Block; import com.reandroid.arsc.io.BlockReader; +import com.reandroid.dex.base.StringKeyItem; import com.reandroid.dex.sections.SectionList; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliFormat; @@ -25,7 +26,7 @@ import java.io.IOException; -public class SectionValue extends DexValue implements SmaliFormat { +public class SectionValue extends DexValueBlock implements SmaliFormat { private final SectionType sectionType; private T mData; @@ -82,6 +83,14 @@ private void updateData(){ void onDataUpdated(T data){ } + @Override + public String getAsString() { + T data = getData(); + if(data instanceof StringKeyItem){ + return ((StringKeyItem) data).getKey(); + } + return null; + } @Override public void append(SmaliWriter writer) throws IOException { T data = getData(); diff --git a/src/main/java/com/reandroid/dex/value/StringValue.java b/src/main/java/com/reandroid/dex/value/StringValue.java index 12065fb5a..b33fbaf70 100644 --- a/src/main/java/com/reandroid/dex/value/StringValue.java +++ b/src/main/java/com/reandroid/dex/value/StringValue.java @@ -15,6 +15,7 @@ */ package com.reandroid.dex.value; +import com.reandroid.dex.common.DexUtils; import com.reandroid.dex.index.StringId; import com.reandroid.dex.item.AnnotationElement; import com.reandroid.dex.item.StringData; @@ -43,4 +44,12 @@ void onDataUpdated(StringId data) { stringData.addStringUsage(usage); } } + @Override + public String getAsString() { + StringData stringData = getStringData(); + if(stringData != null) { + return DexUtils.quoteString(stringData.getString()); + } + return null; + } }