From 6e891ddc2f00e3a0965c116f01ef07ef4489dd55 Mon Sep 17 00:00:00 2001 From: REAndroid Date: Wed, 8 May 2024 22:41:26 +0200 Subject: [PATCH] [DEX] Parse smali code segments --- .../java/com/reandroid/dex/data/CodeItem.java | 4 +- .../reandroid/dex/data/InstructionList.java | 25 ++++++- .../reandroid/dex/debug/DebugSequence.java | 5 +- .../com/reandroid/dex/model/DexClass.java | 52 +++++++++++++ .../smali/model/SmaliAnnotationDirectory.java | 39 ---------- .../dex/smali/model/SmaliAnnotationItem.java | 41 +++++++--- .../dex/smali/model/SmaliAnnotationSet.java | 17 +++-- .../reandroid/dex/smali/model/SmaliClass.java | 7 ++ .../dex/smali/model/SmaliCodeSet.java | 41 ++++++---- .../reandroid/dex/smali/model/SmaliDef.java | 29 ++++++++ .../dex/smali/model/SmaliDefSet.java | 40 ++++++---- .../reandroid/dex/smali/model/SmaliField.java | 16 ++-- .../dex/smali/model/SmaliFieldSet.java | 12 ++- .../dex/smali/model/SmaliInstruction.java | 22 +++++- .../dex/smali/model/SmaliInterfaceSet.java | 22 +++--- .../dex/smali/model/SmaliMethod.java | 15 ++-- .../dex/smali/model/SmaliMethodSet.java | 12 ++- .../dex/smali/model/SmaliParamSet.java | 12 +-- .../dex/smali/model/SmaliRegisterSet.java | 36 +++++---- .../reandroid/dex/smali/model/SmaliSet.java | 74 ++++++++++++++----- 20 files changed, 352 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationDirectory.java diff --git a/src/main/java/com/reandroid/dex/data/CodeItem.java b/src/main/java/com/reandroid/dex/data/CodeItem.java index 888b26f3e..2ff38ad09 100644 --- a/src/main/java/com/reandroid/dex/data/CodeItem.java +++ b/src/main/java/com/reandroid/dex/data/CodeItem.java @@ -239,7 +239,7 @@ public void merge(CodeItem codeItem){ public void fromSmali(SmaliMethod smaliMethod) throws IOException { setRegistersCount(smaliMethod.getRegistersCount()); setParameterRegistersCount(smaliMethod.getParameterRegistersCount()); - getInstructionList().fromSmali(smaliMethod); + getInstructionList().fromSmali(smaliMethod.getCodeSet()); Iterator iterator = smaliMethod.getTryItems(); TryBlock tryBlock = null; if(iterator.hasNext()){ @@ -250,7 +250,7 @@ public void fromSmali(SmaliMethod smaliMethod) throws IOException { } if(smaliMethod.hasDebugs()){ DebugInfo debugInfo = getOrCreateDebugInfo(); - debugInfo.getDebugSequence().fromSmali(smaliMethod); + debugInfo.getDebugSequence().fromSmali(smaliMethod.getCodeSet()); } } @Override diff --git a/src/main/java/com/reandroid/dex/data/InstructionList.java b/src/main/java/com/reandroid/dex/data/InstructionList.java index b163ae1c6..8f85c9118 100644 --- a/src/main/java/com/reandroid/dex/data/InstructionList.java +++ b/src/main/java/com/reandroid/dex/data/InstructionList.java @@ -34,8 +34,8 @@ import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.smali.SmaliFormat; import com.reandroid.dex.smali.SmaliWriter; +import com.reandroid.dex.smali.model.SmaliCodeSet; import com.reandroid.dex.smali.model.SmaliInstruction; -import com.reandroid.dex.smali.model.SmaliMethod; import com.reandroid.utils.CompareUtil; import com.reandroid.utils.collection.*; @@ -319,6 +319,9 @@ public ConstString createStringAt(int index, int register, StringKey value) { return constNumber; } public T1 createAt(int index, Opcode opcode) { + if(index == getCount()) { + return createNext(opcode); + } T1 item = opcode.newInstance(); add(index, item); return item; @@ -645,12 +648,26 @@ public void merge(InstructionList instructionList){ getInsArray().trimToSize(); updateAddresses(); } - public void fromSmali(SmaliMethod smaliMethod) throws IOException { - Iterator iterator = smaliMethod.getInstructions(); + public void fromSmali(SmaliCodeSet smaliCodeSet) throws IOException { + int index = 0; + int offset = smaliCodeSet.getAddressOffset(); + if(offset != 0) { + Ins ins = getAtAddress(smaliCodeSet.getAddressOffset()); + if(ins != null) { + index = ins.getIndex(); + }else if(offset >= getCodeUnits()) { + index = getCount(); + } + } + fromSmali(index, smaliCodeSet); + } + public void fromSmali(int index, SmaliCodeSet smaliCodeSet) throws IOException { + Iterator iterator = smaliCodeSet.getInstructions(); while (iterator.hasNext()){ SmaliInstruction smaliInstruction = iterator.next(); - Ins ins = createNext(smaliInstruction.getOpcode()); + Ins ins = createAt(index, smaliInstruction.getOpcode()); ins.fromSmali(smaliInstruction); + index ++; } getInsArray().trimToSize(); updateAddresses(); diff --git a/src/main/java/com/reandroid/dex/debug/DebugSequence.java b/src/main/java/com/reandroid/dex/debug/DebugSequence.java index a35d55745..680ce942e 100644 --- a/src/main/java/com/reandroid/dex/debug/DebugSequence.java +++ b/src/main/java/com/reandroid/dex/debug/DebugSequence.java @@ -21,6 +21,7 @@ import com.reandroid.dex.base.FixedDexContainer; import com.reandroid.dex.data.InstructionList; import com.reandroid.dex.id.IdItem; +import com.reandroid.dex.smali.model.SmaliCodeSet; import com.reandroid.dex.smali.model.SmaliDebug; import com.reandroid.dex.smali.model.SmaliMethod; import com.reandroid.utils.collection.*; @@ -293,8 +294,8 @@ public void merge(DebugSequence sequence){ cacheValues(); getElementList().trimToSize(); } - public void fromSmali(SmaliMethod smaliMethod) throws IOException { - Iterator iterator = smaliMethod.getDebugs(); + public void fromSmali(SmaliCodeSet smaliCodeSet) throws IOException { + Iterator iterator = smaliCodeSet.getDebugs(); while (iterator.hasNext()){ SmaliDebug smaliDebug = iterator.next(); DebugElementType type = smaliDebug.getDebugElementType(); diff --git a/src/main/java/com/reandroid/dex/model/DexClass.java b/src/main/java/com/reandroid/dex/model/DexClass.java index 99b44b2f6..60b713801 100644 --- a/src/main/java/com/reandroid/dex/model/DexClass.java +++ b/src/main/java/com/reandroid/dex/model/DexClass.java @@ -21,7 +21,10 @@ import com.reandroid.dex.id.IdItem; import com.reandroid.dex.key.*; import com.reandroid.dex.reference.TypeListReference; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; +import com.reandroid.dex.smali.model.SmaliField; +import com.reandroid.dex.smali.model.SmaliMethod; import com.reandroid.dex.value.DexValueBlock; import com.reandroid.dex.value.DexValueType; import com.reandroid.dex.value.NullValue; @@ -302,6 +305,12 @@ public Iterator getInstanceFields() { } return ComputeIterator.of(classData.getInstanceFields(), this::createField); } + public DexField getOrCreateInstanceField(FieldKey fieldKey){ + return createField(getOrCreateInstance(fieldKey)); + } + public FieldDef getOrCreateInstance(FieldKey fieldKey){ + return getOrCreateClassData().getOrCreateInstance(fieldKey); + } public Iterator getDeclaredMethods(Predicate filter) { Iterator iterator = getDeclaredMethods(); if(filter == null){ @@ -620,6 +629,49 @@ public void append(SmaliWriter writer) throws IOException { getClassData(); getId().append(writer); } + + public DexMethod parseMethod(SmaliReader reader) throws IOException { + SmaliMethod smaliMethod = new SmaliMethod(); + smaliMethod.setDefining(getKey()); + smaliMethod.parse(reader); + MethodKey methodKey = smaliMethod.getKey(); + DexMethod dexMethod; + if(smaliMethod.isDirect()) { + dexMethod = getOrCreateDirectMethod(methodKey); + } else { + dexMethod = getOrCreateVirtualMethod(methodKey); + } + dexMethod.getDefinition().fromSmali(smaliMethod); + return dexMethod; + } + public DexField parseField(SmaliReader reader) throws IOException { + SmaliField smaliField = new SmaliField(); + smaliField.setDefining(getKey()); + smaliField.parse(reader); + FieldKey fieldKey = smaliField.getKey(); + DexField dexField; + if(smaliField.isInstance()) { + dexField = getOrCreateInstanceField(fieldKey); + } else { + dexField = getOrCreateStaticField(fieldKey); + } + dexField.getDefinition().fromSmali(smaliField); + return dexField; + } + public DexField parseInterfaces(SmaliReader reader) throws IOException { + SmaliField smaliField = new SmaliField(); + smaliField.setDefining(getKey()); + smaliField.parse(reader); + FieldKey fieldKey = smaliField.getKey(); + DexField dexField; + if(smaliField.isInstance()) { + dexField = getOrCreateInstanceField(fieldKey); + } else { + dexField = getOrCreateStaticField(fieldKey); + } + dexField.getDefinition().fromSmali(smaliField); + return dexField; + } public void writeSmali(SmaliWriter writer, File dir) throws IOException { File file = toSmaliFile(dir); IOUtil.writeUtf8(toSmali(writer), file); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationDirectory.java b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationDirectory.java deleted file mode 100644 index 327e6b1ff..000000000 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationDirectory.java +++ /dev/null @@ -1,39 +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.smali.model; - -import com.reandroid.dex.smali.SmaliDirective; -import com.reandroid.dex.smali.SmaliReader; - -import java.io.IOException; - -public class SmaliAnnotationDirectory extends SmaliSet{ - public SmaliAnnotationDirectory(){ - super(); - } - - @Override - public void parse(SmaliReader reader) throws IOException { - SmaliDirective directive = SmaliDirective.parse(reader, false); - while (directive == SmaliDirective.ANNOTATION){ - SmaliAnnotationSet annotationSet = new SmaliAnnotationSet(); - add(annotationSet); - annotationSet.parse(reader); - reader.skipWhitespacesOrComment(); - directive = SmaliDirective.parse(reader, false); - } - } -} diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationItem.java b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationItem.java index ee3322afc..ff7f470f3 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationItem.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationItem.java @@ -29,6 +29,7 @@ public class SmaliAnnotationItem extends SmaliSet implem public SmaliAnnotationItem(){ super(); + this.smaliDirective = SmaliDirective.ANNOTATION; } public AnnotationVisibility getVisibility() { @@ -50,8 +51,16 @@ public SmaliDirective getSmaliDirective() { return smaliDirective; } - public void setSmaliDirective(SmaliDirective smaliDirective) { - this.smaliDirective = smaliDirective; + public void setSmaliDirective(SmaliDirective annotationDirective) { + if(annotationDirective == null) { + throw new NullPointerException("Null annotation directive"); + } + if(annotationDirective != SmaliDirective.ANNOTATION && + annotationDirective != SmaliDirective.SUB_ANNOTATION) { + throw new IllegalArgumentException("Invalid annotation directive: " + + annotationDirective); + } + this.smaliDirective = annotationDirective; } @Override @@ -77,19 +86,33 @@ public void parse(SmaliReader reader) throws IOException{ setSmaliDirective(directive); setVisibility(AnnotationVisibility.parse(reader)); setType(TypeKey.read(reader)); - while (parseElements(reader)){ + while (parseNext(reader) != null){ reader.skipWhitespacesOrComment(); } SmaliParseException.expect(reader, getSmaliDirective(), true); } - private boolean parseElements(SmaliReader reader) throws IOException { + @Override + SmaliAnnotationElement createNext(SmaliReader reader) { reader.skipWhitespacesOrComment(); + if(reader.finished()) { + return null; + } if(getSmaliDirective().isEnd(reader)){ - return false; + return null; + } + return new SmaliAnnotationElement(); + } + + public static SmaliAnnotationItem read(SmaliReader reader) throws IOException { + reader.skipWhitespacesOrComment(); + if(reader.finished()) { + return null; + } + SmaliAnnotationItem smali = new SmaliAnnotationItem(); + smali.parse(reader); + if(!smali.isEmpty()) { + return smali; } - SmaliAnnotationElement element = new SmaliAnnotationElement(); - add(element); - element.parse(reader); - return true; + return null; } } 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 03a0b56be..66dc56b01 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliAnnotationSet.java @@ -33,14 +33,7 @@ public void append(SmaliWriter writer) throws IOException { } @Override - public void parse(SmaliReader reader) throws IOException{ - SmaliAnnotationItem item; - while ((item = createNext(reader)) != null){ - add(item); - item.parse(reader); - } - } - private SmaliAnnotationItem createNext(SmaliReader reader){ + SmaliAnnotationItem createNext(SmaliReader reader) { reader.skipWhitespacesOrComment(); SmaliDirective directive = SmaliDirective.parse(reader, false); if(directive != SmaliDirective.ANNOTATION && directive != SmaliDirective.SUB_ANNOTATION){ @@ -52,4 +45,12 @@ private SmaliAnnotationItem createNext(SmaliReader reader){ } return new SmaliAnnotationItem(); } + public static SmaliAnnotationSet read(SmaliReader reader) throws IOException { + SmaliAnnotationSet smali = new SmaliAnnotationSet(); + smali.parse(reader); + if(!smali.isEmpty()) { + return smali; + } + return null; + } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliClass.java b/src/main/java/com/reandroid/dex/smali/model/SmaliClass.java index bcb8a33dd..2f367c9e3 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliClass.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliClass.java @@ -189,6 +189,13 @@ private void parseSource(SmaliReader reader) throws IOException{ setSourceFile(StringKey.read(reader)); } + public SmaliField parseField(SmaliReader reader) throws IOException { + return fields.parseNext(reader); + } + public SmaliMethod parseMethod(SmaliReader reader) throws IOException { + return methods.parseNext(reader); + } + @Override public String toDebugString() { return "class = " + getKey(); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliCodeSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliCodeSet.java index 5bf097129..3bd246f23 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliCodeSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliCodeSet.java @@ -19,19 +19,27 @@ import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; -import com.reandroid.utils.collection.InstanceIterator; import java.io.IOException; import java.util.Iterator; -public class SmaliCodeSet extends SmaliSet{ +public class SmaliCodeSet extends SmaliSet { + + private int addressOffset; public SmaliCodeSet(){ super(); } - public void updateAddresses(){ - int address = 0; + public int getAddressOffset() { + return addressOffset; + } + public void setAddressOffset(int addressOffset) { + this.addressOffset = addressOffset; + } + + public void updateAddresses() { + int address = getAddressOffset(); Iterator iterator = getInstructions(); while (iterator.hasNext()) { SmaliInstruction ins = iterator.next(); @@ -40,10 +48,19 @@ public void updateAddresses(){ } } public Iterator getInstructions() { - return InstanceIterator.of(iterator(), SmaliInstruction.class); + return iterator(SmaliInstruction.class); + } + public Iterator getTryItems() { + return iterator(SmaliCodeTryItem.class); } public Iterator getDebugs() { - return InstanceIterator.of(iterator(), SmaliDebug.class); + return iterator(SmaliDebug.class); + } + public void clearInstructions() { + removeInstances(SmaliDebug.class); + } + public void clearDebugs() { + removeInstances(SmaliDebug.class); } @Override @@ -57,15 +74,11 @@ public void append(SmaliWriter writer) throws IOException { @Override public void parse(SmaliReader reader) throws IOException { - SmaliCode code; - while ((code = createNext(reader)) != null){ - add(code); - code.parse(reader); - reader.skipWhitespacesOrComment(); - } + super.parse(reader); updateAddresses(); } - private SmaliCode createNext(SmaliReader reader){ + @Override + SmaliCode createNext(SmaliReader reader) { SmaliDirective directive = SmaliDirective.parse(reader, false); if(directive != null){ return createFor(directive); @@ -80,7 +93,7 @@ private SmaliCode createNext(SmaliReader reader){ } return null; } - public static SmaliCode createFor(SmaliDirective directive){ + private static SmaliCode createFor(SmaliDirective directive){ if(directive == SmaliDirective.LINE){ return new SmaliLineNumber(); } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliDef.java b/src/main/java/com/reandroid/dex/smali/model/SmaliDef.java index 7d8d27329..36e53ba34 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliDef.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliDef.java @@ -18,15 +18,21 @@ import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.common.Modifier; import com.reandroid.dex.key.Key; +import com.reandroid.dex.key.TypeKey; import com.reandroid.dex.smali.SmaliDirective; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliRegion; +import java.io.IOException; + public abstract class SmaliDef extends Smali implements SmaliRegion { private String name; private AccessFlag[] accessFlags; private SmaliAnnotationSet annotation; + private TypeKey defining; + public SmaliDef(){ super(); } @@ -47,6 +53,19 @@ public String getName() { public void setName(String name) { this.name = name; } + public TypeKey getDefining() { + TypeKey typeKey = this.defining; + if(typeKey == null) { + SmaliDefSet defSet = getDefSet(); + if(defSet != null) { + typeKey = defSet.getDefining(); + } + } + return typeKey; + } + public void setDefining(TypeKey defining) { + this.defining = defining; + } public SmaliAnnotationSet getAnnotation() { return annotation; @@ -81,7 +100,17 @@ public boolean isPrivate(){ return Modifier.contains(getAccessFlags(), AccessFlag.PRIVATE); } + private SmaliDefSet getDefSet(){ + return getParentInstance(SmaliMethodSet.class); + } public SmaliClass getSmaliClass(){ + if(getClass().isInstance(SmaliClass.class)) { + return (SmaliClass) this; + } return getParentInstance(SmaliClass.class); } + + public SmaliAnnotationItem parseAnnotation(SmaliReader reader) throws IOException { + return getOrCreateAnnotation().parseNext(reader); + } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliDefSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliDefSet.java index eb3bf543b..189fd9718 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliDefSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliDefSet.java @@ -1,5 +1,6 @@ package com.reandroid.dex.smali.model; +import com.reandroid.dex.key.TypeKey; import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliRegion; @@ -10,11 +11,31 @@ public abstract class SmaliDefSet extends SmaliSet implements SmaliRegion { + private TypeKey defining; + public SmaliDefSet(){ super(); } - public abstract T createNew(); + public TypeKey getDefining() { + TypeKey typeKey = this.defining; + if(typeKey == null) { + SmaliClass smaliClass = getSmaliClass(); + if(smaliClass != null){ + typeKey = smaliClass.getKey(); + } + } + return typeKey; + } + public void setDefining(TypeKey defining) { + this.defining = defining; + } + + abstract T createNew(); + + public SmaliClass getSmaliClass(){ + return getParentInstance(SmaliClass.class); + } @Override public void append(SmaliWriter writer) throws IOException { @@ -22,23 +43,12 @@ public void append(SmaliWriter writer) throws IOException { } @Override - public void parse(SmaliReader reader) throws IOException { - while (parseNext(reader)){ - reader.skipWhitespaces(); - } - } - private boolean parseNext(SmaliReader reader) throws IOException { - if(reader.finished()){ - return false; - } + T createNext(SmaliReader reader) { reader.skipWhitespacesOrComment(); SmaliDirective directive = SmaliDirective.parse(reader, false); if(directive != getSmaliDirective()){ - return false; + return null; } - T item = createNew(); - add(item); - item.parse(reader); - return true; + return createNew(); } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliField.java b/src/main/java/com/reandroid/dex/smali/model/SmaliField.java index 6fd792f03..709431513 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliField.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliField.java @@ -35,12 +35,11 @@ public SmaliField(){ super(); } - @Override public FieldKey getKey(){ - SmaliClass smaliClass = getSmaliClass(); - if(smaliClass != null){ - return getKey(smaliClass.getKey()); + TypeKey defining = getDefining(); + if(defining != null){ + return getKey(defining); } return null; } @@ -121,6 +120,9 @@ private void parseName(SmaliReader reader) { } private void parseValue(SmaliReader reader) throws IOException { reader.skipWhitespaces(); + if(reader.finished()) { + return; + } if(reader.get() != '='){ return; } @@ -151,9 +153,9 @@ private void parseAnnotationSet(SmaliReader reader) throws IOException { @Override public String toDebugString() { StringBuilder builder = new StringBuilder(); - SmaliClass smaliClass = getSmaliClass(); - if(smaliClass != null){ - builder.append(smaliClass.toDebugString()); + TypeKey typeKey = getDefining(); + if(typeKey != null){ + builder.append(typeKey); builder.append(", "); } builder.append("field = "); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliFieldSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliFieldSet.java index 758b2d722..958cb3430 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliFieldSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliFieldSet.java @@ -16,6 +16,7 @@ package com.reandroid.dex.smali.model; import com.reandroid.dex.smali.SmaliDirective; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.collection.FilterIterator; @@ -62,11 +63,20 @@ public void append(SmaliWriter writer) throws IOException { } @Override - public SmaliField createNew() { + SmaliField createNew() { return new SmaliField(); } @Override public SmaliDirective getSmaliDirective() { return SmaliDirective.FIELD; } + + public static SmaliFieldSet read(SmaliReader reader) throws IOException { + SmaliFieldSet smali = new SmaliFieldSet(); + smali.parse(reader); + if(!smali.isEmpty()) { + return smali; + } + return null; + } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliInstruction.java b/src/main/java/com/reandroid/dex/smali/model/SmaliInstruction.java index 94d0839d0..b5caefbae 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliInstruction.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliInstruction.java @@ -41,6 +41,20 @@ public SmaliInstruction(){ this.registerSet = SmaliRegisterSet.NO_REGISTER_SET; this.operand = SmaliInstructionOperand.NO_OPERAND; } + public SmaliInstruction(Opcode opcode){ + super(); + if(opcode == null) { + throw new NullPointerException(); + } + this.opcode = opcode; + initRegisterSet(opcode); + try { + initOperand(opcode); + } catch (IOException exception) { + // Will not happen + throw new RuntimeException(exception); + } + } public Key getKey(){ SmaliInstructionOperand operand = getOperand(); @@ -124,7 +138,7 @@ public boolean hasLabel(SmaliLabel label){ public Opcode getOpcode() { return opcode; } - public void initializeOpcode(Opcode opcode) { + public void initializeOpcode(Opcode opcode) throws IOException { this.opcode = opcode; initRegisterSet(opcode); initOperand(opcode); @@ -139,7 +153,7 @@ private void initRegisterSet(Opcode opcode) { } setRegisterSet(registerSet); } - private void initOperand(Opcode opcode) { + private void initOperand(Opcode opcode) throws IOException { OperandType operandType = opcode.getOperandType(); SmaliInstructionOperand operand; if(operandType == OperandType.NONE){ @@ -153,7 +167,7 @@ private void initOperand(Opcode opcode) { }else if(operandType == OperandType.DECIMAL){ operand = new SmaliInstructionOperand.SmaliDecimalOperand(); }else { - throw new RuntimeException("Unknown operand type: " + operandType + throw new IOException("Unknown operand type: " + operandType + ", opcode = " + opcode); } setOperand(operand); @@ -191,7 +205,7 @@ public void parse(SmaliReader reader) throws IOException { } getOperand().parse(opcode, reader); } - private Opcode parseOpcode(SmaliReader reader){ + private Opcode parseOpcode(SmaliReader reader) throws IOException { reader.skipWhitespaces(); Opcode opcode = Opcode.parseSmali(reader, true); initializeOpcode(opcode); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliInterfaceSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliInterfaceSet.java index 8a02c3b02..461dc7fde 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliInterfaceSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliInterfaceSet.java @@ -58,20 +58,20 @@ public void append(SmaliWriter writer) throws IOException { writer.appendAll(iterator()); } @Override - public void parse(SmaliReader reader) throws IOException { - while (parseNext(reader)){ - reader.skipWhitespaces(); - } - } - private boolean parseNext(SmaliReader reader) throws IOException { + SmaliInterface createNext(SmaliReader reader) { reader.skipWhitespacesOrComment(); SmaliDirective directive = SmaliDirective.parse(reader, false); if(directive != getSmaliDirective()){ - return false; + return null; + } + return new SmaliInterface(); + } + public static SmaliInterfaceSet read(SmaliReader reader) throws IOException { + SmaliInterfaceSet smali = new SmaliInterfaceSet(); + smali.parse(reader); + if(!smali.isEmpty()) { + return smali; } - SmaliInterface item = new SmaliInterface(); - add(item); - item.parse(reader); - return true; + return null; } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliMethod.java b/src/main/java/com/reandroid/dex/smali/model/SmaliMethod.java index ac5f516d6..37b231e85 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliMethod.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliMethod.java @@ -48,9 +48,9 @@ public SmaliMethod(){ @Override public MethodKey getKey(){ - SmaliClass smaliClass = getSmaliClass(); - if(smaliClass != null){ - return getKey(smaliClass.getKey()); + TypeKey typeKey = getDefining(); + if(typeKey != null) { + return getKey(typeKey); } return null; } @@ -101,7 +101,7 @@ public SmaliCodeSet getCodeSet() { return codeSet; } public Iterator getTryItems(){ - return getCodeSet().iterator(SmaliCodeTryItem.class); + return getCodeSet().getTryItems(); } @Override @@ -145,6 +145,7 @@ public void append(SmaliWriter writer) throws IOException { @Override public void parse(SmaliReader reader) throws IOException { + reader.skipWhitespacesOrComment(); SmaliParseException.expect(reader, getSmaliDirective()); setAccessFlags(AccessFlag.parse(reader)); parseName(reader); @@ -222,9 +223,9 @@ public int getLocalRegistersCount() { @Override public String toDebugString() { StringBuilder builder = new StringBuilder(); - SmaliClass smaliClass = getSmaliClass(); - if(smaliClass != null){ - builder.append(smaliClass.toDebugString()); + TypeKey typeKey = getDefining(); + if(typeKey != null){ + builder.append(typeKey); builder.append(", "); } builder.append("method = "); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliMethodSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliMethodSet.java index 79e03c317..5849fc380 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliMethodSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliMethodSet.java @@ -16,6 +16,7 @@ package com.reandroid.dex.smali.model; import com.reandroid.dex.smali.SmaliDirective; +import com.reandroid.dex.smali.SmaliReader; import com.reandroid.dex.smali.SmaliWriter; import com.reandroid.utils.collection.FilterIterator; @@ -36,7 +37,7 @@ public Iterator getVirtualMethods() { } @Override - public SmaliMethod createNew() { + SmaliMethod createNew() { return new SmaliMethod(); } @Override @@ -69,4 +70,13 @@ public void append(SmaliWriter writer) throws IOException { appendOnce = true; } } + + public static SmaliMethodSet read(SmaliReader reader) throws IOException { + SmaliMethodSet smali = new SmaliMethodSet(); + smali.parse(reader); + if(!smali.isEmpty()) { + return smali; + } + return null; + } } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliParamSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliParamSet.java index 09ed499a7..c93e00670 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliParamSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliParamSet.java @@ -18,8 +18,6 @@ import com.reandroid.dex.smali.SmaliDirective; import com.reandroid.dex.smali.SmaliReader; -import java.io.IOException; - public class SmaliParamSet extends SmaliSet{ public SmaliParamSet(){ @@ -27,15 +25,7 @@ public SmaliParamSet(){ } @Override - public void parse(SmaliReader reader) throws IOException { - SmaliMethodParameter param; - while ((param = createNext(reader)) != null){ - add(param); - param.parse(reader); - reader.skipWhitespacesOrComment(); - } - } - private SmaliMethodParameter createNext(SmaliReader reader){ + SmaliMethodParameter createNext(SmaliReader reader) { SmaliDirective directive = SmaliDirective.parse(reader, false); if(directive == SmaliDirective.PARAM){ return new SmaliMethodParameter(); diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliRegisterSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliRegisterSet.java index 6ae39bc2a..ed4b58d3d 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliRegisterSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliRegisterSet.java @@ -94,6 +94,16 @@ public void parse(SmaliReader reader) throws IOException { parseRegisters(reader); } } + + @Override + public SmaliRegister parseNext(SmaliReader reader) throws IOException { + SmaliRegister register = new SmaliRegister(); + add(register); + register.parse(reader); + reader.skipWhitespacesOrComment(); + return register; + } + private void parseRegisters(SmaliReader reader) throws IOException { reader.skipWhitespacesOrComment(); int size = getFormat().size(); @@ -102,10 +112,7 @@ private void parseRegisters(SmaliReader reader) throws IOException { SmaliParseException.expect(reader, ','); reader.skipWhitespacesOrComment(); } - SmaliRegister register = new SmaliRegister(); - add(register); - register.parse(reader); - reader.skipWhitespacesOrComment(); + parseNext(reader); } } private void parseOutRegisters(SmaliReader reader) throws IOException { @@ -118,10 +125,7 @@ private void parseOutRegisters(SmaliReader reader) throws IOException { SmaliParseException.expect(reader, ','); reader.skipWhitespacesOrComment(); } - SmaliRegister register = new SmaliRegister(); - add(register); - register.parse(reader); - reader.skipWhitespacesOrComment(); + parseNext(reader); parsedOnce = true; } SmaliParseException.expect(reader, '}'); @@ -131,19 +135,15 @@ private void parseOutRangeRegisters(SmaliReader reader) throws IOException { SmaliParseException.expect(reader, '{'); reader.skipWhitespacesOrComment(); - SmaliRegister register1 = new SmaliRegister(); - add(register1); - register1.parse(reader); + // first register + parseNext(reader); - reader.skipWhitespacesOrComment(); SmaliParseException.expect(reader, '.'); SmaliParseException.expect(reader, '.'); reader.skipWhitespacesOrComment(); - SmaliRegister register2 = new SmaliRegister(); - add(register2); - register2.parse(reader); - reader.skipWhitespacesOrComment(); + // second register + parseNext(reader); SmaliParseException.expect(reader, '}'); } @@ -168,6 +168,10 @@ public void append(SmaliWriter writer) { public void parse(SmaliReader reader) { } @Override + public SmaliRegister parseNext(SmaliReader reader) { + return null; + } + @Override public String toString() { return ""; } diff --git a/src/main/java/com/reandroid/dex/smali/model/SmaliSet.java b/src/main/java/com/reandroid/dex/smali/model/SmaliSet.java index b0503f047..32b94e421 100644 --- a/src/main/java/com/reandroid/dex/smali/model/SmaliSet.java +++ b/src/main/java/com/reandroid/dex/smali/model/SmaliSet.java @@ -21,7 +21,7 @@ import java.io.IOException; import java.util.Iterator; -import java.util.List; +import java.util.function.Predicate; public class SmaliSet extends Smali{ @@ -29,7 +29,8 @@ public class SmaliSet extends Smali{ public SmaliSet(){ super(); - body = new ArrayCollection<>(); + this.body = new ArrayCollection<>(); + this.body.setMonitor(new SmaliSetMonitor<>(this)); } public Iterator iterator() { @@ -55,28 +56,22 @@ public T get(int i) { return body.get(i); } public boolean add(T smali){ - boolean added = body.add(smali); - if(smali != null){ - smali.setParent(this); - } - return added; + return body.add(smali); } public boolean contains(T smali){ return body.contains(smali); } public boolean remove(T smali){ - boolean removed = body.remove(smali); - if(removed && smali != null){ - smali.setParent(null); - } - return removed; + return body.remove(smali); } - public T remove(int i){ - T smali = body.remove(i); - if(smali != null){ - smali.setParent(null); - } - return smali; + public T remove(int i) { + return body.remove(i); + } + public boolean removeIf(Predicate filter){ + return body.removeIf(filter); + } + public boolean removeInstances(Class instance){ + return body.removeIf(instance::isInstance); } public void clear(){ for (T smali : body) { @@ -84,6 +79,16 @@ public void clear(){ } body.clear(); } + void onRemoved(T item) { + if(item != null) { + item.setParent(null); + } + } + void onAdded(T item) { + if(item != null) { + item.setParent(this); + } + } @Override public void append(SmaliWriter writer) throws IOException { writer.appendAllWithDoubleNewLine(iterator()); @@ -91,6 +96,39 @@ public void append(SmaliWriter writer) throws IOException { @Override public void parse(SmaliReader reader) throws IOException { + while (parseNext(reader) != null) { + reader.skipWhitespacesOrComment(); + } + } + public T parseNext(SmaliReader reader) throws IOException { + if(reader.finished()) { + return null; + } + T item = createNext(reader); + if(item != null) { + add(item); + item.parse(reader); + } + return item; + } + T createNext(SmaliReader reader) { + throw new RuntimeException("Method not implemented"); + } + static class SmaliSetMonitor implements ArrayCollection.Monitor { + + private final SmaliSet smaliSet; + + SmaliSetMonitor(SmaliSet smaliSet){ + this.smaliSet = smaliSet; + } + @Override + public void onAdd(int i, T item) { + smaliSet.onAdded(item); + } + @Override + public void onRemoved(int i, T item) { + smaliSet.onRemoved(item); + } } }