mirror of
https://github.com/revanced/smali.git
synced 2025-05-31 04:40:12 +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,10 +699,26 @@ public abstract class DexWriter<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException {
|
private static class CodeItemOffset<MethodKey> {
|
||||||
debugSectionOffset = writer.getPosition();
|
@Nonnull MethodKey method;
|
||||||
|
int codeOffset;
|
||||||
|
|
||||||
|
private CodeItemOffset(@Nonnull MethodKey method, int codeOffset) {
|
||||||
|
this.codeOffset = codeOffset;
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter,
|
||||||
|
@Nonnull DeferredOutputStream temp) throws IOException {
|
||||||
|
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
|
||||||
|
debugSectionOffset = offsetWriter.getPosition();
|
||||||
DebugWriter<StringKey, TypeKey> debugWriter =
|
DebugWriter<StringKey, TypeKey> debugWriter =
|
||||||
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
|
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, offsetWriter);
|
||||||
|
|
||||||
|
DexDataWriter codeWriter = new DexDataWriter(temp, 0);
|
||||||
|
|
||||||
|
List<CodeItemOffset<MethodKey>> codeOffsets = Lists.newArrayList();
|
||||||
|
|
||||||
for (ClassKey classKey: classSection.getSortedClasses()) {
|
for (ClassKey classKey: classSection.getSortedClasses()) {
|
||||||
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
||||||
@ -720,6 +727,29 @@ 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) {
|
||||||
|
int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, methodKey);
|
||||||
|
int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, debugItemOffset);
|
||||||
|
|
||||||
|
if (codeItemOffset != NO_OFFSET) {
|
||||||
|
codeOffsets.add(new CodeItemOffset<MethodKey>(methodKey, codeItemOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 DebugItem> debugItems = classSection.getDebugItems(methodKey);
|
||||||
Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
|
Iterable<? extends StringKey> parameterNames = classSection.getParameterNames(methodKey);
|
||||||
|
|
||||||
@ -735,12 +765,12 @@ public abstract class DexWriter<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (debugItems == null && parameterCount == 0) {
|
if (debugItems == null && parameterCount == 0) {
|
||||||
continue;
|
return NO_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
numDebugInfoItems++;
|
numDebugInfoItems++;
|
||||||
|
|
||||||
classSection.setDebugItemOffset(methodKey, writer.getPosition());
|
int debugItemOffset = writer.getPosition();
|
||||||
int startingLineNumber = 0;
|
int startingLineNumber = 0;
|
||||||
|
|
||||||
if (debugItems != null) {
|
if (debugItems != null) {
|
||||||
@ -774,32 +804,25 @@ public abstract class DexWriter<
|
|||||||
}
|
}
|
||||||
// write an END_SEQUENCE opcode, to end the debug item
|
// write an END_SEQUENCE opcode, to end the debug item
|
||||||
writer.write(0);
|
writer.write(0);
|
||||||
}
|
|
||||||
}
|
return debugItemOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
|
private int writeCodeItem(@Nonnull DexDataWriter writer,
|
||||||
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
|
@Nonnull ByteArrayOutputStream ehBuf,
|
||||||
|
@Nonnull MethodKey methodKey,
|
||||||
writer.align();
|
int debugItemOffset) throws IOException {
|
||||||
codeSectionOffset = writer.getPosition();
|
|
||||||
for (ClassKey classKey: classSection.getSortedClasses()) {
|
|
||||||
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
|
||||||
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
|
|
||||||
|
|
||||||
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
|
|
||||||
|
|
||||||
for (MethodKey methodKey: methods) {
|
|
||||||
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
|
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
|
||||||
int debugItemOffset = classSection.getDebugItemOffset(methodKey);
|
|
||||||
|
|
||||||
if (instructions == null && debugItemOffset == NO_OFFSET) {
|
if (instructions == null && debugItemOffset == NO_OFFSET) {
|
||||||
continue;
|
return NO_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
numCodeItemItems++;
|
numCodeItemItems++;
|
||||||
|
|
||||||
writer.align();
|
writer.align();
|
||||||
|
|
||||||
|
int codeItemOffset = writer.getPosition();
|
||||||
classSection.setCodeItemOffset(methodKey, writer.getPosition());
|
classSection.setCodeItemOffset(methodKey, writer.getPosition());
|
||||||
|
|
||||||
writer.writeUshort(classSection.getRegisterCount(methodKey));
|
writer.writeUshort(classSection.getRegisterCount(methodKey));
|
||||||
@ -935,8 +958,6 @@ public abstract class DexWriter<
|
|||||||
if (tryBlocks.size() > 0) {
|
if (tryBlocks.size() > 0) {
|
||||||
writer.align();
|
writer.align();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// filter out unique lists of exception handlers
|
// filter out unique lists of exception handlers
|
||||||
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
|
Map<List<? extends ExceptionHandler>, Integer> exceptionHandlerOffsetMap = Maps.newHashMap();
|
||||||
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
|
||||||
@ -1005,8 +1026,8 @@ public abstract class DexWriter<
|
|||||||
writer.writeInt(debugItemOffset);
|
writer.writeInt(debugItemOffset);
|
||||||
writer.writeInt(0);
|
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();
|
||||||
|
@ -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