mirror of
https://github.com/revanced/smali.git
synced 2025-05-30 04:30:13 +02:00
Generify the IO requirements for writing a dex file
The DexWriter implementations now write to a generic "DexDataStore", instead of writing directly to a file. Also, writing of the DebugItems and CodeItems are linked, with the code items being written to a temporary location, and then the entire code item section is written as a batch after the debug item section.
This commit is contained in:
parent
160449b83a
commit
99b46173c5
@ -91,8 +91,5 @@ public interface ClassSection<StringKey extends CharSequence, TypeKey extends Ch
|
|||||||
void setCodeItemOffset(@Nonnull MethodKey key, int offset);
|
void setCodeItemOffset(@Nonnull MethodKey key, int offset);
|
||||||
int getCodeItemOffset(@Nonnull MethodKey key);
|
int getCodeItemOffset(@Nonnull MethodKey key);
|
||||||
|
|
||||||
void setDebugItemOffset(@Nonnull MethodKey key, int offset);
|
|
||||||
int getDebugItemOffset(@Nonnull MethodKey key);
|
|
||||||
|
|
||||||
void writeDebugItem(@Nonnull DebugWriter<StringKey, TypeKey> writer, DebugItem debugItem) throws IOException;
|
void writeDebugItem(@Nonnull DebugWriter<StringKey, TypeKey> writer, DebugItem debugItem) throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,7 @@
|
|||||||
|
|
||||||
package org.jf.dexlib2.writer;
|
package org.jf.dexlib2.writer;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.*;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Ordering;
|
|
||||||
import org.jf.dexlib2.AccessFlags;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib2.ReferenceType;
|
import org.jf.dexlib2.ReferenceType;
|
||||||
import org.jf.dexlib2.base.BaseAnnotation;
|
import org.jf.dexlib2.base.BaseAnnotation;
|
||||||
@ -50,19 +47,22 @@ import org.jf.dexlib2.iface.instruction.formats.*;
|
|||||||
import org.jf.dexlib2.iface.reference.*;
|
import org.jf.dexlib2.iface.reference.*;
|
||||||
import org.jf.dexlib2.util.InstructionUtil;
|
import org.jf.dexlib2.util.InstructionUtil;
|
||||||
import org.jf.dexlib2.util.MethodUtil;
|
import org.jf.dexlib2.util.MethodUtil;
|
||||||
|
import org.jf.dexlib2.writer.io.DeferredOutputStream;
|
||||||
|
import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory;
|
||||||
|
import org.jf.dexlib2.writer.io.DexDataStore;
|
||||||
|
import org.jf.dexlib2.writer.io.MemoryDeferredOutputStream;
|
||||||
import org.jf.dexlib2.writer.util.TryListBuilder;
|
import org.jf.dexlib2.writer.util.TryListBuilder;
|
||||||
import org.jf.util.CollectionUtils;
|
import org.jf.util.CollectionUtils;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
import org.jf.util.RandomAccessFileOutputStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -189,14 +189,17 @@ public abstract class DexWriter<
|
|||||||
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
|
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo(String path) throws IOException {
|
public void writeTo(@Nonnull DexDataStore dest) throws IOException {
|
||||||
RandomAccessFile raf = new RandomAccessFile(path, "rw");
|
this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
|
||||||
raf.setLength(0);
|
}
|
||||||
|
|
||||||
|
public void writeTo(@Nonnull DexDataStore dest,
|
||||||
|
@Nonnull DeferredOutputStreamFactory tempFactory) throws IOException {
|
||||||
try {
|
try {
|
||||||
int dataSectionOffset = getDataSectionOffset();
|
int dataSectionOffset = getDataSectionOffset();
|
||||||
DexDataWriter headerWriter = outputAt(raf, 0);
|
DexDataWriter headerWriter = outputAt(dest, 0);
|
||||||
DexDataWriter indexWriter = outputAt(raf, HeaderItem.ITEM_SIZE);
|
DexDataWriter indexWriter = outputAt(dest, HeaderItem.ITEM_SIZE);
|
||||||
DexDataWriter offsetWriter = outputAt(raf, dataSectionOffset);
|
DexDataWriter offsetWriter = outputAt(dest, dataSectionOffset);
|
||||||
try {
|
try {
|
||||||
writeStrings(indexWriter, offsetWriter);
|
writeStrings(indexWriter, offsetWriter);
|
||||||
writeTypes(indexWriter);
|
writeTypes(indexWriter);
|
||||||
@ -209,8 +212,7 @@ public abstract class DexWriter<
|
|||||||
writeAnnotationSets(offsetWriter);
|
writeAnnotationSets(offsetWriter);
|
||||||
writeAnnotationSetRefs(offsetWriter);
|
writeAnnotationSetRefs(offsetWriter);
|
||||||
writeAnnotationDirectories(offsetWriter);
|
writeAnnotationDirectories(offsetWriter);
|
||||||
writeDebugItems(offsetWriter);
|
writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream());
|
||||||
writeCodeItems(offsetWriter);
|
|
||||||
writeClasses(indexWriter, offsetWriter);
|
writeClasses(indexWriter, offsetWriter);
|
||||||
writeMapItem(offsetWriter);
|
writeMapItem(offsetWriter);
|
||||||
writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
|
writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
|
||||||
@ -219,15 +221,14 @@ public abstract class DexWriter<
|
|||||||
indexWriter.close();
|
indexWriter.close();
|
||||||
offsetWriter.close();
|
offsetWriter.close();
|
||||||
}
|
}
|
||||||
FileChannel fileChannel = raf.getChannel();
|
updateSignature(dest);
|
||||||
updateSignature(fileChannel);
|
updateChecksum(dest);
|
||||||
updateChecksum(fileChannel);
|
|
||||||
} finally {
|
} finally {
|
||||||
raf.close();
|
dest.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSignature(FileChannel fileChannel) throws IOException {
|
private void updateSignature(@Nonnull DexDataStore dataStore) throws IOException {
|
||||||
MessageDigest md;
|
MessageDigest md;
|
||||||
try {
|
try {
|
||||||
md = MessageDigest.getInstance("SHA-1");
|
md = MessageDigest.getInstance("SHA-1");
|
||||||
@ -235,14 +236,12 @@ public abstract class DexWriter<
|
|||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
|
byte[] buffer = new byte[4 * 1024];
|
||||||
fileChannel.position(HeaderItem.HEADER_SIZE_OFFSET);
|
InputStream input = dataStore.readAt(HeaderItem.HEADER_SIZE_OFFSET);
|
||||||
int bytesRead = fileChannel.read(buffer);
|
int bytesRead = input.read(buffer);
|
||||||
while (bytesRead >= 0) {
|
while (bytesRead >= 0) {
|
||||||
buffer.rewind();
|
md.update(buffer, 0, bytesRead);
|
||||||
md.update(buffer);
|
bytesRead = input.read(buffer);
|
||||||
buffer.clear();
|
|
||||||
bytesRead = fileChannel.read(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] signature = md.digest();
|
byte[] signature = md.digest();
|
||||||
@ -251,38 +250,30 @@ public abstract class DexWriter<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write signature
|
// write signature
|
||||||
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
|
OutputStream output = dataStore.outputAt(HeaderItem.SIGNATURE_OFFSET);
|
||||||
fileChannel.write(ByteBuffer.wrap(signature));
|
output.write(signature);
|
||||||
|
output.close();
|
||||||
// flush
|
|
||||||
fileChannel.force(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChecksum(FileChannel fileChannel) throws IOException {
|
private void updateChecksum(@Nonnull DexDataStore dataStore) throws IOException {
|
||||||
Adler32 a32 = new Adler32();
|
Adler32 a32 = new Adler32();
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
|
byte[] buffer = new byte[4 * 1024];
|
||||||
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
|
InputStream input = dataStore.readAt(HeaderItem.SIGNATURE_OFFSET);
|
||||||
int bytesRead = fileChannel.read(buffer);
|
int bytesRead = input.read(buffer);
|
||||||
while (bytesRead >= 0) {
|
while (bytesRead >= 0) {
|
||||||
a32.update(buffer.array(), 0, bytesRead);
|
a32.update(buffer, 0, bytesRead);
|
||||||
buffer.clear();
|
bytesRead = input.read(buffer);
|
||||||
bytesRead = fileChannel.read(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write checksum, utilizing logic in DexWriter to write the integer value properly
|
// write checksum, utilizing logic in DexWriter to write the integer value properly
|
||||||
fileChannel.position(HeaderItem.CHECKSUM_OFFSET);
|
OutputStream output = dataStore.outputAt(HeaderItem.CHECKSUM_OFFSET);
|
||||||
int checksum = (int) a32.getValue();
|
DexDataWriter.writeInt(output, (int)a32.getValue());
|
||||||
ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream();
|
output.close();
|
||||||
DexDataWriter.writeInt(checksumBuf, checksum);
|
|
||||||
fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray()));
|
|
||||||
|
|
||||||
// flush
|
|
||||||
fileChannel.force(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DexDataWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException {
|
private static DexDataWriter outputAt(DexDataStore dataStore, int filePosition) throws IOException {
|
||||||
return new DexDataWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition);
|
return new DexDataWriter(dataStore.outputAt(filePosition), filePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
|
private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException {
|
||||||
@ -708,81 +699,27 @@ public abstract class DexWriter<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException {
|
private static class CodeItemOffset<MethodKey> {
|
||||||
debugSectionOffset = writer.getPosition();
|
@Nonnull MethodKey method;
|
||||||
DebugWriter<StringKey, TypeKey> debugWriter =
|
int codeOffset;
|
||||||
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
|
|
||||||
|
|
||||||
for (ClassKey classKey: classSection.getSortedClasses()) {
|
private CodeItemOffset(@Nonnull MethodKey method, int codeOffset) {
|
||||||
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
this.codeOffset = codeOffset;
|
||||||
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
|
this.method = method;
|
||||||
|
|
||||||
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
|
|
||||||
|
|
||||||
for (MethodKey methodKey: methods) {
|
|
||||||
Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
|
|
||||||
Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
|
|
||||||
|
|
||||||
int parameterCount = 0;
|
|
||||||
if (parameterNames != null) {
|
|
||||||
int index = 0;
|
|
||||||
for (StringKey parameterName: parameterNames) {
|
|
||||||
index++;
|
|
||||||
if (parameterName != null) {
|
|
||||||
parameterCount = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debugItems == null && parameterCount == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
numDebugInfoItems++;
|
|
||||||
|
|
||||||
classSection.setDebugItemOffset(methodKey, writer.getPosition());
|
|
||||||
int startingLineNumber = 0;
|
|
||||||
|
|
||||||
if (debugItems != null) {
|
|
||||||
for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
|
|
||||||
if (debugItem instanceof LineNumber) {
|
|
||||||
startingLineNumber = ((LineNumber)debugItem).getLineNumber();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.writeUleb128(startingLineNumber);
|
|
||||||
|
|
||||||
writer.writeUleb128(parameterCount);
|
|
||||||
if (parameterNames != null) {
|
|
||||||
int index = 0;
|
|
||||||
for (StringKey parameterName: parameterNames) {
|
|
||||||
if (index == parameterCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debugItems != null) {
|
|
||||||
debugWriter.reset(startingLineNumber);
|
|
||||||
|
|
||||||
for (DebugItem debugItem: debugItems) {
|
|
||||||
classSection.writeDebugItem(debugWriter, debugItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// write an END_SEQUENCE opcode, to end the debug item
|
|
||||||
writer.write(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
|
private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter,
|
||||||
|
@Nonnull DeferredOutputStream temp) throws IOException {
|
||||||
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
|
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
|
||||||
|
debugSectionOffset = offsetWriter.getPosition();
|
||||||
|
DebugWriter<StringKey, TypeKey> debugWriter =
|
||||||
|
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, offsetWriter);
|
||||||
|
|
||||||
|
DexDataWriter codeWriter = new DexDataWriter(temp, 0);
|
||||||
|
|
||||||
|
List<CodeItemOffset<MethodKey>> codeOffsets = Lists.newArrayList();
|
||||||
|
|
||||||
writer.align();
|
|
||||||
codeSectionOffset = writer.getPosition();
|
|
||||||
for (ClassKey classKey: classSection.getSortedClasses()) {
|
for (ClassKey classKey: classSection.getSortedClasses()) {
|
||||||
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
||||||
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
|
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
|
||||||
@ -790,223 +727,307 @@ public abstract class DexWriter<
|
|||||||
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
|
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
|
||||||
|
|
||||||
for (MethodKey methodKey: methods) {
|
for (MethodKey methodKey: methods) {
|
||||||
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
|
int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, methodKey);
|
||||||
int debugItemOffset = classSection.getDebugItemOffset(methodKey);
|
int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, debugItemOffset);
|
||||||
|
|
||||||
if (instructions == null && debugItemOffset == NO_OFFSET) {
|
if (codeItemOffset != NO_OFFSET) {
|
||||||
continue;
|
codeOffsets.add(new CodeItemOffset<MethodKey>(methodKey, codeItemOffset));
|
||||||
}
|
|
||||||
|
|
||||||
numCodeItemItems++;
|
|
||||||
|
|
||||||
writer.align();
|
|
||||||
classSection.setCodeItemOffset(methodKey, writer.getPosition());
|
|
||||||
|
|
||||||
writer.writeUshort(classSection.getRegisterCount(methodKey));
|
|
||||||
|
|
||||||
boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
|
|
||||||
Collection<? extends TypeKey> parameters = typeListSection.getTypes(
|
|
||||||
protoSection.getParameters(methodSection.getPrototype(methodKey)));
|
|
||||||
|
|
||||||
List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey);
|
|
||||||
writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
|
|
||||||
|
|
||||||
if (instructions != null) {
|
|
||||||
tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
|
|
||||||
|
|
||||||
int outParamCount = 0;
|
|
||||||
int codeUnitCount = 0;
|
|
||||||
for (Instruction instruction: instructions) {
|
|
||||||
codeUnitCount += instruction.getCodeUnits();
|
|
||||||
if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
|
|
||||||
ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
|
|
||||||
MethodReference methodRef = (MethodReference)refInsn.getReference();
|
|
||||||
int paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(instruction.getOpcode()));
|
|
||||||
if (paramCount > outParamCount) {
|
|
||||||
outParamCount = paramCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeUshort(outParamCount);
|
|
||||||
writer.writeUshort(tryBlocks.size());
|
|
||||||
writer.writeInt(debugItemOffset);
|
|
||||||
|
|
||||||
InstructionWriter instructionWriter =
|
|
||||||
InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
|
|
||||||
methodSection);
|
|
||||||
|
|
||||||
writer.writeInt(codeUnitCount);
|
|
||||||
for (Instruction instruction: instructions) {
|
|
||||||
switch (instruction.getOpcode().format) {
|
|
||||||
case Format10t:
|
|
||||||
instructionWriter.write((Instruction10t)instruction);
|
|
||||||
break;
|
|
||||||
case Format10x:
|
|
||||||
instructionWriter.write((Instruction10x)instruction);
|
|
||||||
break;
|
|
||||||
case Format11n:
|
|
||||||
instructionWriter.write((Instruction11n)instruction);
|
|
||||||
break;
|
|
||||||
case Format11x:
|
|
||||||
instructionWriter.write((Instruction11x)instruction);
|
|
||||||
break;
|
|
||||||
case Format12x:
|
|
||||||
instructionWriter.write((Instruction12x)instruction);
|
|
||||||
break;
|
|
||||||
case Format20bc:
|
|
||||||
instructionWriter.write((Instruction20bc)instruction);
|
|
||||||
break;
|
|
||||||
case Format20t:
|
|
||||||
instructionWriter.write((Instruction20t)instruction);
|
|
||||||
break;
|
|
||||||
case Format21c:
|
|
||||||
instructionWriter.write((Instruction21c)instruction);
|
|
||||||
break;
|
|
||||||
case Format21ih:
|
|
||||||
instructionWriter.write((Instruction21ih)instruction);
|
|
||||||
break;
|
|
||||||
case Format21lh:
|
|
||||||
instructionWriter.write((Instruction21lh)instruction);
|
|
||||||
break;
|
|
||||||
case Format21s:
|
|
||||||
instructionWriter.write((Instruction21s)instruction);
|
|
||||||
break;
|
|
||||||
case Format21t:
|
|
||||||
instructionWriter.write((Instruction21t)instruction);
|
|
||||||
break;
|
|
||||||
case Format22b:
|
|
||||||
instructionWriter.write((Instruction22b)instruction);
|
|
||||||
break;
|
|
||||||
case Format22c:
|
|
||||||
instructionWriter.write((Instruction22c)instruction);
|
|
||||||
break;
|
|
||||||
case Format22s:
|
|
||||||
instructionWriter.write((Instruction22s)instruction);
|
|
||||||
break;
|
|
||||||
case Format22t:
|
|
||||||
instructionWriter.write((Instruction22t)instruction);
|
|
||||||
break;
|
|
||||||
case Format22x:
|
|
||||||
instructionWriter.write((Instruction22x)instruction);
|
|
||||||
break;
|
|
||||||
case Format23x:
|
|
||||||
instructionWriter.write((Instruction23x)instruction);
|
|
||||||
break;
|
|
||||||
case Format30t:
|
|
||||||
instructionWriter.write((Instruction30t)instruction);
|
|
||||||
break;
|
|
||||||
case Format31c:
|
|
||||||
instructionWriter.write((Instruction31c)instruction);
|
|
||||||
break;
|
|
||||||
case Format31i:
|
|
||||||
instructionWriter.write((Instruction31i)instruction);
|
|
||||||
break;
|
|
||||||
case Format31t:
|
|
||||||
instructionWriter.write((Instruction31t)instruction);
|
|
||||||
break;
|
|
||||||
case Format32x:
|
|
||||||
instructionWriter.write((Instruction32x)instruction);
|
|
||||||
break;
|
|
||||||
case Format35c:
|
|
||||||
instructionWriter.write((Instruction35c)instruction);
|
|
||||||
break;
|
|
||||||
case Format3rc:
|
|
||||||
instructionWriter.write((Instruction3rc)instruction);
|
|
||||||
break;
|
|
||||||
case Format51l:
|
|
||||||
instructionWriter.write((Instruction51l)instruction);
|
|
||||||
break;
|
|
||||||
case ArrayPayload:
|
|
||||||
instructionWriter.write((ArrayPayload)instruction);
|
|
||||||
break;
|
|
||||||
case PackedSwitchPayload:
|
|
||||||
instructionWriter.write((PackedSwitchPayload)instruction);
|
|
||||||
break;
|
|
||||||
case SparseSwitchPayload:
|
|
||||||
instructionWriter.write((SparseSwitchPayload)instruction);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ExceptionWithContext("Unsupported instruction format: %s",
|
|
||||||
instruction.getOpcode().format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryBlocks.size() > 0) {
|
|
||||||
writer.align();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// filter out unique lists of exception handlers
|
|
||||||
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
|
|
||||||
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
|
||||||
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
|
|
||||||
}
|
|
||||||
DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
|
|
||||||
|
|
||||||
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
|
||||||
int startAddress = tryBlock.getStartCodeAddress();
|
|
||||||
int endAddress = startAddress + tryBlock.getCodeUnitCount();
|
|
||||||
|
|
||||||
int tbCodeUnitCount = endAddress - startAddress;
|
|
||||||
|
|
||||||
writer.writeInt(startAddress);
|
|
||||||
writer.writeUshort(tbCodeUnitCount);
|
|
||||||
|
|
||||||
if (tryBlock.getExceptionHandlers().size() == 0) {
|
|
||||||
throw new ExceptionWithContext("No exception handlers for the try block!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
|
|
||||||
if (offset != 0) {
|
|
||||||
// exception handler has already been written out, just use it
|
|
||||||
writer.writeUshort(offset);
|
|
||||||
} else {
|
|
||||||
// if offset has not been set yet, we are about to write out a new exception handler
|
|
||||||
offset = ehBuf.size();
|
|
||||||
writer.writeUshort(offset);
|
|
||||||
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
|
|
||||||
|
|
||||||
// check if the last exception handler is a catch-all and adjust the size accordingly
|
|
||||||
int ehSize = tryBlock.getExceptionHandlers().size();
|
|
||||||
ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
|
|
||||||
if (ehLast.getExceptionType() == null) {
|
|
||||||
ehSize = ehSize * (-1) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now let's layout the exception handlers, assuming that catch-all is always last
|
|
||||||
DexDataWriter.writeSleb128(ehBuf, ehSize);
|
|
||||||
for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
|
|
||||||
TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
|
|
||||||
|
|
||||||
int codeAddress = eh.getHandlerCodeAddress();
|
|
||||||
|
|
||||||
if (exceptionTypeKey != null) {
|
|
||||||
//regular exception handling
|
|
||||||
DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
|
|
||||||
DexDataWriter.writeUleb128(ehBuf, codeAddress);
|
|
||||||
} else {
|
|
||||||
//catch-all
|
|
||||||
DexDataWriter.writeUleb128(ehBuf, codeAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ehBuf.size() > 0) {
|
|
||||||
ehBuf.writeTo(writer);
|
|
||||||
ehBuf.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no instructions, all we have is the debug item offset
|
|
||||||
writer.writeUshort(0);
|
|
||||||
writer.writeUshort(0);
|
|
||||||
writer.writeInt(debugItemOffset);
|
|
||||||
writer.writeInt(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offsetWriter.align();
|
||||||
|
codeSectionOffset = offsetWriter.getPosition();
|
||||||
|
|
||||||
|
temp.writeTo(offsetWriter);
|
||||||
|
temp.close();
|
||||||
|
|
||||||
|
for (CodeItemOffset<MethodKey> codeOffset: codeOffsets) {
|
||||||
|
classSection.setCodeItemOffset(codeOffset.method, codeSectionOffset + codeOffset.codeOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeDebugItem(@Nonnull DexDataWriter writer,
|
||||||
|
@Nonnull DebugWriter<StringKey, TypeKey> debugWriter,
|
||||||
|
@Nonnull MethodKey methodKey) throws IOException {
|
||||||
|
Iterable<? extends DebugItem> debugItems = classSection.getDebugItems(methodKey);
|
||||||
|
Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
|
||||||
|
|
||||||
|
int parameterCount = 0;
|
||||||
|
if (parameterNames != null) {
|
||||||
|
int index = 0;
|
||||||
|
for (StringKey parameterName: parameterNames) {
|
||||||
|
index++;
|
||||||
|
if (parameterName != null) {
|
||||||
|
parameterCount = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugItems == null && parameterCount == 0) {
|
||||||
|
return NO_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
numDebugInfoItems++;
|
||||||
|
|
||||||
|
int debugItemOffset = writer.getPosition();
|
||||||
|
int startingLineNumber = 0;
|
||||||
|
|
||||||
|
if (debugItems != null) {
|
||||||
|
for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) {
|
||||||
|
if (debugItem instanceof LineNumber) {
|
||||||
|
startingLineNumber = ((LineNumber)debugItem).getLineNumber();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.writeUleb128(startingLineNumber);
|
||||||
|
|
||||||
|
writer.writeUleb128(parameterCount);
|
||||||
|
if (parameterNames != null) {
|
||||||
|
int index = 0;
|
||||||
|
for (StringKey parameterName: parameterNames) {
|
||||||
|
if (index == parameterCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugItems != null) {
|
||||||
|
debugWriter.reset(startingLineNumber);
|
||||||
|
|
||||||
|
for (DebugItem debugItem: debugItems) {
|
||||||
|
classSection.writeDebugItem(debugWriter, debugItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// write an END_SEQUENCE opcode, to end the debug item
|
||||||
|
writer.write(0);
|
||||||
|
|
||||||
|
return debugItemOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeCodeItem(@Nonnull DexDataWriter writer,
|
||||||
|
@Nonnull ByteArrayOutputStream ehBuf,
|
||||||
|
@Nonnull MethodKey methodKey,
|
||||||
|
int debugItemOffset) throws IOException {
|
||||||
|
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
|
||||||
|
|
||||||
|
if (instructions == null && debugItemOffset == NO_OFFSET) {
|
||||||
|
return NO_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
numCodeItemItems++;
|
||||||
|
|
||||||
|
writer.align();
|
||||||
|
|
||||||
|
int codeItemOffset = writer.getPosition();
|
||||||
|
classSection.setCodeItemOffset(methodKey, writer.getPosition());
|
||||||
|
|
||||||
|
writer.writeUshort(classSection.getRegisterCount(methodKey));
|
||||||
|
|
||||||
|
boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey));
|
||||||
|
Collection<? extends TypeKey> parameters = typeListSection.getTypes(
|
||||||
|
protoSection.getParameters(methodSection.getPrototype(methodKey)));
|
||||||
|
|
||||||
|
List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = classSection.getTryBlocks(methodKey);
|
||||||
|
writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic));
|
||||||
|
|
||||||
|
if (instructions != null) {
|
||||||
|
tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks);
|
||||||
|
|
||||||
|
int outParamCount = 0;
|
||||||
|
int codeUnitCount = 0;
|
||||||
|
for (Instruction instruction: instructions) {
|
||||||
|
codeUnitCount += instruction.getCodeUnits();
|
||||||
|
if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
|
||||||
|
ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
|
||||||
|
MethodReference methodRef = (MethodReference)refInsn.getReference();
|
||||||
|
int paramCount = MethodUtil.getParameterRegisterCount(methodRef, InstructionUtil.isInvokeStatic(instruction.getOpcode()));
|
||||||
|
if (paramCount > outParamCount) {
|
||||||
|
outParamCount = paramCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.writeUshort(outParamCount);
|
||||||
|
writer.writeUshort(tryBlocks.size());
|
||||||
|
writer.writeInt(debugItemOffset);
|
||||||
|
|
||||||
|
InstructionWriter instructionWriter =
|
||||||
|
InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
|
||||||
|
methodSection);
|
||||||
|
|
||||||
|
writer.writeInt(codeUnitCount);
|
||||||
|
for (Instruction instruction: instructions) {
|
||||||
|
switch (instruction.getOpcode().format) {
|
||||||
|
case Format10t:
|
||||||
|
instructionWriter.write((Instruction10t)instruction);
|
||||||
|
break;
|
||||||
|
case Format10x:
|
||||||
|
instructionWriter.write((Instruction10x)instruction);
|
||||||
|
break;
|
||||||
|
case Format11n:
|
||||||
|
instructionWriter.write((Instruction11n)instruction);
|
||||||
|
break;
|
||||||
|
case Format11x:
|
||||||
|
instructionWriter.write((Instruction11x)instruction);
|
||||||
|
break;
|
||||||
|
case Format12x:
|
||||||
|
instructionWriter.write((Instruction12x)instruction);
|
||||||
|
break;
|
||||||
|
case Format20bc:
|
||||||
|
instructionWriter.write((Instruction20bc)instruction);
|
||||||
|
break;
|
||||||
|
case Format20t:
|
||||||
|
instructionWriter.write((Instruction20t)instruction);
|
||||||
|
break;
|
||||||
|
case Format21c:
|
||||||
|
instructionWriter.write((Instruction21c)instruction);
|
||||||
|
break;
|
||||||
|
case Format21ih:
|
||||||
|
instructionWriter.write((Instruction21ih)instruction);
|
||||||
|
break;
|
||||||
|
case Format21lh:
|
||||||
|
instructionWriter.write((Instruction21lh)instruction);
|
||||||
|
break;
|
||||||
|
case Format21s:
|
||||||
|
instructionWriter.write((Instruction21s)instruction);
|
||||||
|
break;
|
||||||
|
case Format21t:
|
||||||
|
instructionWriter.write((Instruction21t)instruction);
|
||||||
|
break;
|
||||||
|
case Format22b:
|
||||||
|
instructionWriter.write((Instruction22b)instruction);
|
||||||
|
break;
|
||||||
|
case Format22c:
|
||||||
|
instructionWriter.write((Instruction22c)instruction);
|
||||||
|
break;
|
||||||
|
case Format22s:
|
||||||
|
instructionWriter.write((Instruction22s)instruction);
|
||||||
|
break;
|
||||||
|
case Format22t:
|
||||||
|
instructionWriter.write((Instruction22t)instruction);
|
||||||
|
break;
|
||||||
|
case Format22x:
|
||||||
|
instructionWriter.write((Instruction22x)instruction);
|
||||||
|
break;
|
||||||
|
case Format23x:
|
||||||
|
instructionWriter.write((Instruction23x)instruction);
|
||||||
|
break;
|
||||||
|
case Format30t:
|
||||||
|
instructionWriter.write((Instruction30t)instruction);
|
||||||
|
break;
|
||||||
|
case Format31c:
|
||||||
|
instructionWriter.write((Instruction31c)instruction);
|
||||||
|
break;
|
||||||
|
case Format31i:
|
||||||
|
instructionWriter.write((Instruction31i)instruction);
|
||||||
|
break;
|
||||||
|
case Format31t:
|
||||||
|
instructionWriter.write((Instruction31t)instruction);
|
||||||
|
break;
|
||||||
|
case Format32x:
|
||||||
|
instructionWriter.write((Instruction32x)instruction);
|
||||||
|
break;
|
||||||
|
case Format35c:
|
||||||
|
instructionWriter.write((Instruction35c)instruction);
|
||||||
|
break;
|
||||||
|
case Format3rc:
|
||||||
|
instructionWriter.write((Instruction3rc)instruction);
|
||||||
|
break;
|
||||||
|
case Format51l:
|
||||||
|
instructionWriter.write((Instruction51l)instruction);
|
||||||
|
break;
|
||||||
|
case ArrayPayload:
|
||||||
|
instructionWriter.write((ArrayPayload)instruction);
|
||||||
|
break;
|
||||||
|
case PackedSwitchPayload:
|
||||||
|
instructionWriter.write((PackedSwitchPayload)instruction);
|
||||||
|
break;
|
||||||
|
case SparseSwitchPayload:
|
||||||
|
instructionWriter.write((SparseSwitchPayload)instruction);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ExceptionWithContext("Unsupported instruction format: %s",
|
||||||
|
instruction.getOpcode().format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryBlocks.size() > 0) {
|
||||||
|
writer.align();
|
||||||
|
|
||||||
|
// filter out unique lists of exception handlers
|
||||||
|
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
|
||||||
|
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
||||||
|
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
|
||||||
|
}
|
||||||
|
DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
|
||||||
|
|
||||||
|
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
||||||
|
int startAddress = tryBlock.getStartCodeAddress();
|
||||||
|
int endAddress = startAddress + tryBlock.getCodeUnitCount();
|
||||||
|
|
||||||
|
int tbCodeUnitCount = endAddress - startAddress;
|
||||||
|
|
||||||
|
writer.writeInt(startAddress);
|
||||||
|
writer.writeUshort(tbCodeUnitCount);
|
||||||
|
|
||||||
|
if (tryBlock.getExceptionHandlers().size() == 0) {
|
||||||
|
throw new ExceptionWithContext("No exception handlers for the try block!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers());
|
||||||
|
if (offset != 0) {
|
||||||
|
// exception handler has already been written out, just use it
|
||||||
|
writer.writeUshort(offset);
|
||||||
|
} else {
|
||||||
|
// if offset has not been set yet, we are about to write out a new exception handler
|
||||||
|
offset = ehBuf.size();
|
||||||
|
writer.writeUshort(offset);
|
||||||
|
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset);
|
||||||
|
|
||||||
|
// check if the last exception handler is a catch-all and adjust the size accordingly
|
||||||
|
int ehSize = tryBlock.getExceptionHandlers().size();
|
||||||
|
ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1);
|
||||||
|
if (ehLast.getExceptionType() == null) {
|
||||||
|
ehSize = ehSize * (-1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let's layout the exception handlers, assuming that catch-all is always last
|
||||||
|
DexDataWriter.writeSleb128(ehBuf, ehSize);
|
||||||
|
for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
|
||||||
|
TypeKey exceptionTypeKey = classSection.getExceptionType(eh);
|
||||||
|
|
||||||
|
int codeAddress = eh.getHandlerCodeAddress();
|
||||||
|
|
||||||
|
if (exceptionTypeKey != null) {
|
||||||
|
//regular exception handling
|
||||||
|
DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey));
|
||||||
|
DexDataWriter.writeUleb128(ehBuf, codeAddress);
|
||||||
|
} else {
|
||||||
|
//catch-all
|
||||||
|
DexDataWriter.writeUleb128(ehBuf, codeAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ehBuf.size() > 0) {
|
||||||
|
ehBuf.writeTo(writer);
|
||||||
|
ehBuf.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no instructions, all we have is the debug item offset
|
||||||
|
writer.writeUshort(0);
|
||||||
|
writer.writeUshort(0);
|
||||||
|
writer.writeInt(debugItemOffset);
|
||||||
|
writer.writeInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeItemOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calcNumItems() {
|
private int calcNumItems() {
|
||||||
|
@ -345,14 +345,6 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
|
|||||||
return builderMethod.codeItemOffset;
|
return builderMethod.codeItemOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setDebugItemOffset(@Nonnull BuilderMethod builderMethod, int offset) {
|
|
||||||
builderMethod.debugInfoOffset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int getDebugItemOffset(@Nonnull BuilderMethod builderMethod) {
|
|
||||||
return builderMethod.debugInfoOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) {
|
@Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) {
|
||||||
if (stringReference == null) {
|
if (stringReference == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -49,7 +49,6 @@ public class BuilderMethod extends BaseMethodReference implements Method {
|
|||||||
|
|
||||||
int annotationSetRefListOffset = DexWriter.NO_OFFSET;
|
int annotationSetRefListOffset = DexWriter.NO_OFFSET;
|
||||||
int codeItemOffset = DexWriter.NO_OFFSET;
|
int codeItemOffset = DexWriter.NO_OFFSET;
|
||||||
int debugInfoOffset = DexWriter.NO_OFFSET;
|
|
||||||
|
|
||||||
BuilderMethod(@Nonnull BuilderMethodReference methodReference,
|
BuilderMethod(@Nonnull BuilderMethodReference methodReference,
|
||||||
@Nonnull List<? extends BuilderMethodParameter> parameters,
|
@Nonnull List<? extends BuilderMethodParameter> parameters,
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public abstract class DeferredOutputStream extends OutputStream {
|
||||||
|
public abstract void writeTo(OutputStream output) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface DeferredOutputStreamFactory {
|
||||||
|
DeferredOutputStream makeDeferredOutputStream() throws IOException;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public interface DexDataStore {
|
||||||
|
@Nonnull OutputStream outputAt(int offset);
|
||||||
|
@Nonnull InputStream readAt(int offset);
|
||||||
|
void close() throws IOException;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import org.jf.util.RandomAccessFileInputStream;
|
||||||
|
import org.jf.util.RandomAccessFileOutputStream;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class FileDataStore implements DexDataStore {
|
||||||
|
private final RandomAccessFile raf;
|
||||||
|
|
||||||
|
public FileDataStore(@Nonnull File file) throws FileNotFoundException, IOException {
|
||||||
|
this.raf = new RandomAccessFile(file, "rw");
|
||||||
|
this.raf.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull @Override public OutputStream outputAt(int offset) {
|
||||||
|
return new RandomAccessFileOutputStream(raf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull @Override public InputStream readAt(int offset) {
|
||||||
|
return new RandomAccessFileInputStream(raf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void close() throws IOException {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A deferred output stream that uses a file as its backing store, with a in-memory intermediate buffer.
|
||||||
|
*/
|
||||||
|
public class FileDeferredOutputStream extends DeferredOutputStream {
|
||||||
|
private static final int DEFAULT_BUFFER_SIZE = 4 * 1024;
|
||||||
|
|
||||||
|
@Nonnull private final File backingFile;
|
||||||
|
@Nonnull private final NakedBufferedOutputStream output;
|
||||||
|
private int writtenBytes;
|
||||||
|
|
||||||
|
public FileDeferredOutputStream(@Nonnull File backingFile) throws FileNotFoundException {
|
||||||
|
this(backingFile, DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileDeferredOutputStream(@Nonnull File backingFile, int bufferSize) throws FileNotFoundException {
|
||||||
|
this.backingFile = backingFile;
|
||||||
|
output = new NakedBufferedOutputStream(new FileOutputStream(backingFile), bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void writeTo(@Nonnull OutputStream dest) throws IOException {
|
||||||
|
byte[] outBuf = output.getBuffer();
|
||||||
|
int count = output.getCount();
|
||||||
|
output.resetBuffer();
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
// did we actually write something out to disk?
|
||||||
|
if (count != writtenBytes) {
|
||||||
|
InputStream fis = new FileInputStream(backingFile);
|
||||||
|
ByteStreams.copy(fis, dest);
|
||||||
|
backingFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
dest.write(outBuf, 0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(int i) throws IOException {
|
||||||
|
output.write(i);
|
||||||
|
writtenBytes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(byte[] bytes) throws IOException {
|
||||||
|
output.write(bytes);
|
||||||
|
writtenBytes += bytes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(byte[] bytes, int off, int len) throws IOException {
|
||||||
|
output.write(bytes, off, len);
|
||||||
|
writtenBytes += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void flush() throws IOException {
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void close() throws IOException {
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NakedBufferedOutputStream extends BufferedOutputStream {
|
||||||
|
public NakedBufferedOutputStream(OutputStream outputStream) {
|
||||||
|
super(outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NakedBufferedOutputStream(OutputStream outputStream, int i) {
|
||||||
|
super(outputStream, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetBuffer() {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuffer() {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static DeferredOutputStreamFactory getFactory(@Nullable File containingDirectory) {
|
||||||
|
return getFactory(containingDirectory, DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static DeferredOutputStreamFactory getFactory(@Nullable final File containingDirectory,
|
||||||
|
final int bufferSize) {
|
||||||
|
return new DeferredOutputStreamFactory() {
|
||||||
|
@Override public DeferredOutputStream makeDeferredOutputStream() throws IOException {
|
||||||
|
File tempFile = File.createTempFile("dexlibtmp", null, containingDirectory);
|
||||||
|
return new FileDeferredOutputStream(tempFile, bufferSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package org.jf.dexlib2.writer.io;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A deferred output stream that is stored in memory
|
||||||
|
*/
|
||||||
|
public class MemoryDeferredOutputStream extends DeferredOutputStream {
|
||||||
|
private static final int DEFAULT_BUFFER_SIZE = 16 * 1024;
|
||||||
|
|
||||||
|
private final List<byte[]> buffers = Lists.newArrayList();
|
||||||
|
private byte[] currentBuffer;
|
||||||
|
private int currentPosition;
|
||||||
|
|
||||||
|
public MemoryDeferredOutputStream() {
|
||||||
|
this(DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryDeferredOutputStream(int bufferSize) {
|
||||||
|
currentBuffer = new byte[bufferSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void writeTo(OutputStream output) throws IOException {
|
||||||
|
for (byte[] buffer: buffers) {
|
||||||
|
output.write(buffer);
|
||||||
|
}
|
||||||
|
if (currentPosition > 0) {
|
||||||
|
output.write(currentBuffer, 0, currentPosition);
|
||||||
|
}
|
||||||
|
buffers.clear();
|
||||||
|
currentPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(int i) throws IOException {
|
||||||
|
if (remaining() == 0) {
|
||||||
|
buffers.add(currentBuffer);
|
||||||
|
currentBuffer = new byte[currentBuffer.length];
|
||||||
|
currentPosition = 0;
|
||||||
|
}
|
||||||
|
currentBuffer[currentPosition++] = (byte)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(byte[] bytes) throws IOException {
|
||||||
|
write(bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void write(byte[] bytes, int offset, int length) throws IOException {
|
||||||
|
int remaining = remaining();
|
||||||
|
int written = 0;
|
||||||
|
while (length - written > 0) {
|
||||||
|
int toWrite = Math.min(remaining, length);
|
||||||
|
System.arraycopy(bytes, offset + written, currentBuffer, currentPosition, toWrite);
|
||||||
|
written += toWrite;
|
||||||
|
currentPosition += toWrite;
|
||||||
|
|
||||||
|
remaining = remaining();
|
||||||
|
if (remaining == 0) {
|
||||||
|
buffers.add(currentBuffer);
|
||||||
|
currentBuffer = new byte[currentBuffer.length];
|
||||||
|
currentPosition = 0;
|
||||||
|
remaining = currentBuffer.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int remaining() {
|
||||||
|
return currentBuffer.length - currentPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static DeferredOutputStreamFactory getFactory() {
|
||||||
|
return getFactory(DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static DeferredOutputStreamFactory getFactory(final int bufferSize) {
|
||||||
|
return new DeferredOutputStreamFactory() {
|
||||||
|
@Override public DeferredOutputStream makeDeferredOutputStream() {
|
||||||
|
return new MemoryDeferredOutputStream(bufferSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -465,14 +465,6 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence,
|
|||||||
return method.codeItemOffset;
|
return method.codeItemOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setDebugItemOffset(@Nonnull PoolMethod method, int offset) {
|
|
||||||
method.debugInfoOffset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int getDebugItemOffset(@Nonnull PoolMethod method) {
|
|
||||||
return method.debugInfoOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void writeDebugItem(@Nonnull DebugWriter<CharSequence, CharSequence> writer,
|
@Override public void writeDebugItem(@Nonnull DebugWriter<CharSequence, CharSequence> writer,
|
||||||
DebugItem debugItem) throws IOException {
|
DebugItem debugItem) throws IOException {
|
||||||
switch (debugItem.getDebugItemType()) {
|
switch (debugItem.getDebugItemType()) {
|
||||||
|
@ -40,10 +40,12 @@ import org.jf.dexlib2.iface.reference.*;
|
|||||||
import org.jf.dexlib2.iface.value.*;
|
import org.jf.dexlib2.iface.value.*;
|
||||||
import org.jf.dexlib2.immutable.instruction.ImmutableInstructionFactory;
|
import org.jf.dexlib2.immutable.instruction.ImmutableInstructionFactory;
|
||||||
import org.jf.dexlib2.writer.DexWriter;
|
import org.jf.dexlib2.writer.DexWriter;
|
||||||
|
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||||
import org.jf.dexlib2.writer.pool.ProtoPool.Key;
|
import org.jf.dexlib2.writer.pool.ProtoPool.Key;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -86,7 +88,7 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
|
|||||||
for (ClassDef classDef: input.getClasses()) {
|
for (ClassDef classDef: input.getClasses()) {
|
||||||
((ClassPool)dexPool.classSection).intern(classDef);
|
((ClassPool)dexPool.classSection).intern(classDef);
|
||||||
}
|
}
|
||||||
dexPool.writeTo(path);
|
dexPool.writeTo(new FileDataStore(new File(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
|
@Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
|
||||||
|
@ -48,7 +48,6 @@ class PoolMethod extends BaseMethodReference implements Method {
|
|||||||
@Nonnull private final Method method;
|
@Nonnull private final Method method;
|
||||||
protected int annotationSetRefListOffset = DexPool.NO_OFFSET;
|
protected int annotationSetRefListOffset = DexPool.NO_OFFSET;
|
||||||
protected int codeItemOffset = DexPool.NO_OFFSET;
|
protected int codeItemOffset = DexPool.NO_OFFSET;
|
||||||
protected int debugInfoOffset = DexPool.NO_OFFSET;
|
|
||||||
|
|
||||||
public static final Function<Method, PoolMethod> TRANSFORM = new Function<Method, PoolMethod>() {
|
public static final Function<Method, PoolMethod> TRANSFORM = new Function<Method, PoolMethod>() {
|
||||||
@Override public PoolMethod apply(Method method) {
|
@Override public PoolMethod apply(Method method) {
|
||||||
|
@ -36,6 +36,7 @@ import org.antlr.runtime.tree.CommonTree;
|
|||||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||||
|
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||||
import org.jf.util.ConsoleUtil;
|
import org.jf.util.ConsoleUtil;
|
||||||
import org.jf.util.SmaliHelpFormatter;
|
import org.jf.util.SmaliHelpFormatter;
|
||||||
|
|
||||||
@ -225,7 +226,7 @@ public class main {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
dexBuilder.writeTo(outputDexFile);
|
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
@ -241,14 +242,14 @@ public class main {
|
|||||||
File[] files = dir.listFiles();
|
File[] files = dir.listFiles();
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for(File file: files) {
|
for(File file: files) {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
getSmaliFilesInDir(file, smaliFiles);
|
getSmaliFilesInDir(file, smaliFiles);
|
||||||
} else if (file.getName().endsWith(".smali")) {
|
} else if (file.getName().endsWith(".smali")) {
|
||||||
smaliFiles.add(file);
|
smaliFiles.add(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
|
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
|
||||||
boolean printTokens, boolean allowOdex, int apiLevel)
|
boolean printTokens, boolean allowOdex, int apiLevel)
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.jf.util;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
|
public class RandomAccessFileInputStream extends InputStream {
|
||||||
|
private int filePosition;
|
||||||
|
@Nonnull private final RandomAccessFile raf;
|
||||||
|
|
||||||
|
public RandomAccessFileInputStream(@Nonnull RandomAccessFile raf, int filePosition) {
|
||||||
|
this.filePosition = filePosition;
|
||||||
|
this.raf = raf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int read() throws IOException {
|
||||||
|
raf.seek(filePosition);
|
||||||
|
filePosition++;
|
||||||
|
return raf.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int read(byte[] bytes) throws IOException {
|
||||||
|
raf.seek(filePosition);
|
||||||
|
int bytesRead = raf.read(bytes);
|
||||||
|
filePosition += bytesRead;
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int read(byte[] bytes, int offset, int length) throws IOException {
|
||||||
|
raf.seek(filePosition);
|
||||||
|
int bytesRead = raf.read(bytes, offset, length);
|
||||||
|
filePosition += bytesRead;
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public long skip(long l) throws IOException {
|
||||||
|
int skipBytes = Math.min((int)l, available());
|
||||||
|
filePosition += skipBytes;
|
||||||
|
return skipBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int available() throws IOException {
|
||||||
|
return (int)raf.length() - filePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user