mirror of
https://github.com/revanced/smali.git
synced 2025-05-03 16:14:29 +02:00
Add support for writing class_def_items and class_data
This commit is contained in:
parent
f3c33259dd
commit
e68daf22aa
298
dexlib2/src/main/java/org/jf/dexlib2/writer/ClassDefPool.java
Normal file
298
dexlib2/src/main/java/org/jf/dexlib2/writer/ClassDefPool.java
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.dexlib2.writer;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import org.jf.dexlib2.iface.*;
|
||||||
|
import org.jf.dexlib2.util.FieldUtil;
|
||||||
|
import org.jf.dexlib2.util.MethodUtil;
|
||||||
|
import org.jf.util.ExceptionWithContext;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ClassDefPool {
|
||||||
|
public final static int CLASS_DEF_ITEM_SIZE = 0x20;
|
||||||
|
|
||||||
|
@Nonnull private final Map<ClassDef, Boolean> internedClassDefItems = Maps.newHashMap();
|
||||||
|
@Nonnull private final Map<String, ClassDef> nameToClassDef = Maps.newHashMap();
|
||||||
|
@Nonnull private final DexFile dexFile;
|
||||||
|
|
||||||
|
private int indexSectionOffset = -1;
|
||||||
|
private int dataSectionOffset = -1;
|
||||||
|
private int classDataCount = 0;
|
||||||
|
|
||||||
|
public ClassDefPool(@Nonnull DexFile dexFile) {
|
||||||
|
this.dexFile = dexFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void intern(@Nonnull ClassDef classDef) {
|
||||||
|
Boolean prev = internedClassDefItems.put(classDef, false);
|
||||||
|
if (prev != null) {
|
||||||
|
// item is already interned, no need to do it again
|
||||||
|
// TODO: add additional handling for the case of interning a modified class
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dexFile.typePool.intern(classDef.getType());
|
||||||
|
dexFile.typePool.internNullable(classDef.getSuperclass());
|
||||||
|
dexFile.typeListPool.intern(ImmutableSortedSet.copyOf(classDef.getInterfaces()));
|
||||||
|
dexFile.stringPool.internNullable(classDef.getSourceFile());
|
||||||
|
dexFile.encodedArrayPool.intern(classDef);
|
||||||
|
dexFile.annotationDirectoryPool.intern(classDef);
|
||||||
|
boolean hasClassData = false;
|
||||||
|
for (Field field: classDef.getFields()) {
|
||||||
|
hasClassData = true;
|
||||||
|
dexFile.fieldPool.intern(field);
|
||||||
|
}
|
||||||
|
for (Method method: classDef.getMethods()) {
|
||||||
|
hasClassData = true;
|
||||||
|
dexFile.methodPool.intern(method);
|
||||||
|
dexFile.codeItemPool.intern(method);
|
||||||
|
}
|
||||||
|
if (hasClassData) {
|
||||||
|
classDataCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameToClassDef.put(classDef.getType(), classDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndexedSectionSize() {
|
||||||
|
return internedClassDefItems.size() * CLASS_DEF_ITEM_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumClassDefItems() {
|
||||||
|
return internedClassDefItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumClassDataItems() {
|
||||||
|
return classDataCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndexSectionOffset() {
|
||||||
|
if (indexSectionOffset < 0) {
|
||||||
|
throw new ExceptionWithContext("Section offset has not been set yet!");
|
||||||
|
}
|
||||||
|
return indexSectionOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataSectionOffset() {
|
||||||
|
if (dataSectionOffset < 0) {
|
||||||
|
throw new ExceptionWithContext("Section offset has not been set yet!");
|
||||||
|
}
|
||||||
|
return dataSectionOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(@Nonnull DexWriter indexWriter, @Nonnull DexWriter offsetWriter) throws IOException {
|
||||||
|
List<ClassDef> classDefs = Lists.newArrayList(internedClassDefItems.keySet());
|
||||||
|
|
||||||
|
indexSectionOffset = indexWriter.getPosition();
|
||||||
|
dataSectionOffset = offsetWriter.getPosition();
|
||||||
|
|
||||||
|
for (ClassDef classDef: classDefs) {
|
||||||
|
writeClass(indexWriter, offsetWriter, classDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeClass(DexWriter indexWriter, DexWriter offsetWriter, ClassDef classDef) throws IOException {
|
||||||
|
if (classDef == null) {
|
||||||
|
// class does not exist in this dex file, cannot write it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean alreadyWritten = internedClassDefItems.put(classDef, true);
|
||||||
|
if (alreadyWritten != null && alreadyWritten) {
|
||||||
|
// class has already been written, no need to write it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first, try to write a superclass
|
||||||
|
ClassDef superClassDef = nameToClassDef.get(classDef.getSuperclass());
|
||||||
|
writeClass(indexWriter, offsetWriter, superClassDef);
|
||||||
|
|
||||||
|
// then, try to write interfaces
|
||||||
|
for (String iface: classDef.getInterfaces()) {
|
||||||
|
ClassDef interfaceClassDef = nameToClassDef.get(iface);
|
||||||
|
writeClass(indexWriter, offsetWriter, interfaceClassDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and finally, write the class itself
|
||||||
|
writeSelf(indexWriter, offsetWriter, classDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSelf(DexWriter indexWriter, DexWriter offsetWriter, ClassDef classDef) throws IOException {
|
||||||
|
indexWriter.writeInt(dexFile.typePool.getIndex(classDef));
|
||||||
|
indexWriter.writeInt(classDef.getAccessFlags());
|
||||||
|
indexWriter.writeInt(dexFile.typePool.getIndexNullable(classDef.getSuperclass()));
|
||||||
|
|
||||||
|
indexWriter.writeInt(dexFile.typeListPool.getOffset(ImmutableSortedSet.copyOf(classDef.getInterfaces())));
|
||||||
|
|
||||||
|
if (classDef.getSourceFile() != null) {
|
||||||
|
indexWriter.writeInt(dexFile.stringPool.getIndexNullable(classDef.getSourceFile()));
|
||||||
|
} else {
|
||||||
|
indexWriter.writeInt(-1); // TODO: this should be replaced by NO_INDEX
|
||||||
|
}
|
||||||
|
|
||||||
|
indexWriter.writeInt(dexFile.annotationDirectoryPool.getOffset(classDef));
|
||||||
|
|
||||||
|
ClassDataItem classDataItem = new ClassDataItem(classDef);
|
||||||
|
|
||||||
|
if (classDataItem.hasData()) {
|
||||||
|
indexWriter.writeInt(offsetWriter.getPosition());
|
||||||
|
classDataItem.write(offsetWriter);
|
||||||
|
} else {
|
||||||
|
indexWriter.writeInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classDataItem.hasStaticFields()) {
|
||||||
|
indexWriter.writeInt(dexFile.encodedArrayPool.getOffset(classDef));
|
||||||
|
} else {
|
||||||
|
indexWriter.writeInt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClassDataItem {
|
||||||
|
ClassDef classDef;
|
||||||
|
List<Field> fields;
|
||||||
|
List<Method> methods;
|
||||||
|
|
||||||
|
int numStaticFields = 0;
|
||||||
|
int numInstanceFields = 0;
|
||||||
|
int numDirectMethods = 0;
|
||||||
|
int numVirtualMethods = 0;
|
||||||
|
|
||||||
|
private ClassDataItem(ClassDef classDef) {
|
||||||
|
this.classDef = classDef;
|
||||||
|
|
||||||
|
fields = Lists.newArrayList(classDef.getFields());
|
||||||
|
Collections.sort(fields);
|
||||||
|
|
||||||
|
methods = Lists.newArrayList(classDef.getMethods());
|
||||||
|
Collections.sort(methods);
|
||||||
|
|
||||||
|
for (Field field: fields) {
|
||||||
|
if (FieldUtil.isStatic(field)) {
|
||||||
|
numStaticFields++;
|
||||||
|
} else {
|
||||||
|
numInstanceFields++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Method method: methods) {
|
||||||
|
if (MethodUtil.isDirect(method)) {
|
||||||
|
numDirectMethods++;
|
||||||
|
} else {
|
||||||
|
numVirtualMethods++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasData() {
|
||||||
|
return (numStaticFields > 0 || numInstanceFields > 0 || numDirectMethods > 0 || numVirtualMethods > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasStaticFields() {
|
||||||
|
return numStaticFields > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeStaticFields(DexWriter writer) throws IOException {
|
||||||
|
int lastIdx = 0;
|
||||||
|
for (Field field: fields) {
|
||||||
|
if (FieldUtil.isStatic(field)) {
|
||||||
|
int idx = dexFile.fieldPool.getIndex(field);
|
||||||
|
writer.writeUleb128(idx - lastIdx);
|
||||||
|
lastIdx = idx;
|
||||||
|
|
||||||
|
writer.writeUleb128(field.getAccessFlags());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeInstanceFields(DexWriter writer) throws IOException {
|
||||||
|
int lastIdx = 0;
|
||||||
|
for (Field field: fields) {
|
||||||
|
if (!FieldUtil.isStatic(field)) {
|
||||||
|
int idx = dexFile.fieldPool.getIndex(field);
|
||||||
|
writer.writeUleb128(idx - lastIdx);
|
||||||
|
lastIdx = idx;
|
||||||
|
|
||||||
|
writer.writeUleb128(field.getAccessFlags());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDirectMethods(DexWriter writer) throws IOException {
|
||||||
|
int lastIdx = 0;
|
||||||
|
for (Method method: methods) {
|
||||||
|
if (MethodUtil.isDirect(method)) {
|
||||||
|
int idx = dexFile.methodPool.getIndex(method);
|
||||||
|
writer.writeUleb128(idx - lastIdx);
|
||||||
|
lastIdx = idx;
|
||||||
|
|
||||||
|
writer.writeUleb128(method.getAccessFlags());
|
||||||
|
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeVirtualMethods(DexWriter writer) throws IOException {
|
||||||
|
int lastIdx = 0;
|
||||||
|
for (Method method: methods) {
|
||||||
|
if (!MethodUtil.isDirect(method)) {
|
||||||
|
int idx = dexFile.methodPool.getIndex(method);
|
||||||
|
writer.writeUleb128(idx - lastIdx);
|
||||||
|
lastIdx = idx;
|
||||||
|
|
||||||
|
writer.writeUleb128(method.getAccessFlags());
|
||||||
|
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(DexWriter writer) throws IOException {
|
||||||
|
writer.writeUleb128(numStaticFields);
|
||||||
|
writer.writeUleb128(numInstanceFields);
|
||||||
|
writer.writeUleb128(numDirectMethods);
|
||||||
|
writer.writeUleb128(numVirtualMethods);
|
||||||
|
|
||||||
|
writeStaticFields(writer);
|
||||||
|
writeInstanceFields(writer);
|
||||||
|
writeDirectMethods(writer);
|
||||||
|
writeVirtualMethods(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,17 +31,9 @@
|
|||||||
|
|
||||||
package org.jf.dexlib2.writer;
|
package org.jf.dexlib2.writer;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
|
||||||
import org.jf.dexlib2.DebugItemType;
|
|
||||||
import org.jf.dexlib2.ReferenceType;
|
|
||||||
import org.jf.dexlib2.ValueType;
|
import org.jf.dexlib2.ValueType;
|
||||||
import org.jf.dexlib2.iface.*;
|
import org.jf.dexlib2.iface.AnnotationElement;
|
||||||
import org.jf.dexlib2.iface.debug.DebugItem;
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
import org.jf.dexlib2.iface.debug.SetSourceFile;
|
|
||||||
import org.jf.dexlib2.iface.debug.StartLocal;
|
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
|
||||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
|
|
||||||
import org.jf.dexlib2.iface.reference.*;
|
|
||||||
import org.jf.dexlib2.iface.value.*;
|
import org.jf.dexlib2.iface.value.*;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
|
|
||||||
@ -49,7 +41,6 @@ import javax.annotation.Nonnull;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class DexFile {
|
public class DexFile {
|
||||||
@ -68,6 +59,7 @@ public class DexFile {
|
|||||||
@Nonnull final AnnotationDirectoryPool annotationDirectoryPool = new AnnotationDirectoryPool(this);
|
@Nonnull final AnnotationDirectoryPool annotationDirectoryPool = new AnnotationDirectoryPool(this);
|
||||||
@Nonnull final DebugInfoPool debugInfoPool = new DebugInfoPool(this);
|
@Nonnull final DebugInfoPool debugInfoPool = new DebugInfoPool(this);
|
||||||
@Nonnull final CodeItemPool codeItemPool = new CodeItemPool(this);
|
@Nonnull final CodeItemPool codeItemPool = new CodeItemPool(this);
|
||||||
|
@Nonnull final ClassDefPool classDefPool = new ClassDefPool(this);
|
||||||
|
|
||||||
@Nonnull private final Set<? extends ClassDef> classes;
|
@Nonnull private final Set<? extends ClassDef> classes;
|
||||||
|
|
||||||
@ -75,7 +67,7 @@ public class DexFile {
|
|||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
|
|
||||||
for (ClassDef classDef: classes) {
|
for (ClassDef classDef: classes) {
|
||||||
internClass(classDef);
|
classDefPool.intern(classDef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,86 +181,6 @@ public class DexFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void internFields(@Nonnull ClassDef classDef) {
|
|
||||||
for (Field field: classDef.getFields()) {
|
|
||||||
fieldPool.intern(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void internMethods(@Nonnull ClassDef classDef) {
|
|
||||||
for (Method method: classDef.getMethods()) {
|
|
||||||
methodPool.intern(method);
|
|
||||||
codeItemPool.intern(method);
|
|
||||||
|
|
||||||
MethodImplementation methodImpl = method.getImplementation();
|
|
||||||
if (methodImpl != null) {
|
|
||||||
internTryBlocks(methodImpl.getTryBlocks());
|
|
||||||
internInstructions(methodImpl.getInstructions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void internDebugItems(Iterable<? extends DebugItem> debugItems) {
|
|
||||||
// TODO: debug_info_items should technically be internable...
|
|
||||||
for (DebugItem debugItem: debugItems) {
|
|
||||||
switch (debugItem.getDebugItemType()) {
|
|
||||||
case DebugItemType.START_LOCAL:
|
|
||||||
StartLocal startLocal = (StartLocal)debugItem;
|
|
||||||
stringPool.internNullable(startLocal.getName());
|
|
||||||
typePool.internNullable(startLocal.getType());
|
|
||||||
stringPool.internNullable(startLocal.getSignature());
|
|
||||||
break;
|
|
||||||
case DebugItemType.SET_SOURCE_FILE:
|
|
||||||
stringPool.internNullable(((SetSourceFile) debugItem).getSourceFile());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void internTryBlocks(List<? extends TryBlock> tryBlocks) {
|
|
||||||
for (TryBlock tryBlock: tryBlocks) {
|
|
||||||
for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
|
|
||||||
typePool.internNullable(handler.getExceptionType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void internInstructions(Iterable<? extends Instruction> instructions) {
|
|
||||||
for (Instruction instruction: instructions) {
|
|
||||||
if (instruction instanceof ReferenceInstruction) {
|
|
||||||
Reference reference = ((ReferenceInstruction)instruction).getReference();
|
|
||||||
switch (instruction.getOpcode().referenceType) {
|
|
||||||
case ReferenceType.STRING:
|
|
||||||
stringPool.intern((StringReference) reference);
|
|
||||||
break;
|
|
||||||
case ReferenceType.TYPE:
|
|
||||||
typePool.intern((TypeReference)reference);
|
|
||||||
break;
|
|
||||||
case ReferenceType.FIELD:
|
|
||||||
fieldPool.intern((FieldReference) reference);
|
|
||||||
break;
|
|
||||||
case ReferenceType.METHOD:
|
|
||||||
methodPool.intern((MethodReference)reference);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ExceptionWithContext("Unrecognized reference type: %d",
|
|
||||||
instruction.getOpcode().referenceType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void internClass(@Nonnull ClassDef classDef) {
|
|
||||||
typePool.intern(classDef.getType());
|
|
||||||
typePool.internNullable(classDef.getSuperclass());
|
|
||||||
typeListPool.intern(ImmutableSortedSet.copyOf(classDef.getInterfaces()));
|
|
||||||
stringPool.internNullable(classDef.getSourceFile());
|
|
||||||
encodedArrayPool.intern(classDef);
|
|
||||||
annotationDirectoryPool.intern(classDef);
|
|
||||||
internFields(classDef);
|
|
||||||
internMethods(classDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTo(@Nonnull String path) throws IOException {
|
private void writeTo(@Nonnull String path) throws IOException {
|
||||||
RandomAccessFile raf = new RandomAccessFile(path, "rw");
|
RandomAccessFile raf = new RandomAccessFile(path, "rw");
|
||||||
try {
|
try {
|
||||||
@ -292,6 +204,7 @@ public class DexFile {
|
|||||||
annotationDirectoryPool.write(offsetWriter);
|
annotationDirectoryPool.write(offsetWriter);
|
||||||
debugInfoPool.write(offsetWriter);
|
debugInfoPool.write(offsetWriter);
|
||||||
codeItemPool.write(offsetWriter);
|
codeItemPool.write(offsetWriter);
|
||||||
|
classDefPool.write(indexWriter, offsetWriter);
|
||||||
} finally {
|
} finally {
|
||||||
indexWriter.close();
|
indexWriter.close();
|
||||||
offsetWriter.close();
|
offsetWriter.close();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user