mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 20:00: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);
|
||||
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;
|
||||
}
|
||||
|
@ -31,10 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.writer;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.*;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
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.util.InstructionUtil;
|
||||
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.util.CollectionUtils;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.util.RandomAccessFileOutputStream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
@ -189,14 +189,17 @@ public abstract class DexWriter<
|
||||
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
|
||||
}
|
||||
|
||||
public void writeTo(String path) throws IOException {
|
||||
RandomAccessFile raf = new RandomAccessFile(path, "rw");
|
||||
raf.setLength(0);
|
||||
public void writeTo(@Nonnull DexDataStore dest) throws IOException {
|
||||
this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
|
||||
}
|
||||
|
||||
public void writeTo(@Nonnull DexDataStore dest,
|
||||
@Nonnull DeferredOutputStreamFactory tempFactory) throws IOException {
|
||||
try {
|
||||
int dataSectionOffset = getDataSectionOffset();
|
||||
DexDataWriter headerWriter = outputAt(raf, 0);
|
||||
DexDataWriter indexWriter = outputAt(raf, HeaderItem.ITEM_SIZE);
|
||||
DexDataWriter offsetWriter = outputAt(raf, dataSectionOffset);
|
||||
DexDataWriter headerWriter = outputAt(dest, 0);
|
||||
DexDataWriter indexWriter = outputAt(dest, HeaderItem.ITEM_SIZE);
|
||||
DexDataWriter offsetWriter = outputAt(dest, dataSectionOffset);
|
||||
try {
|
||||
writeStrings(indexWriter, offsetWriter);
|
||||
writeTypes(indexWriter);
|
||||
@ -209,8 +212,7 @@ public abstract class DexWriter<
|
||||
writeAnnotationSets(offsetWriter);
|
||||
writeAnnotationSetRefs(offsetWriter);
|
||||
writeAnnotationDirectories(offsetWriter);
|
||||
writeDebugItems(offsetWriter);
|
||||
writeCodeItems(offsetWriter);
|
||||
writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream());
|
||||
writeClasses(indexWriter, offsetWriter);
|
||||
writeMapItem(offsetWriter);
|
||||
writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition());
|
||||
@ -219,15 +221,14 @@ public abstract class DexWriter<
|
||||
indexWriter.close();
|
||||
offsetWriter.close();
|
||||
}
|
||||
FileChannel fileChannel = raf.getChannel();
|
||||
updateSignature(fileChannel);
|
||||
updateChecksum(fileChannel);
|
||||
updateSignature(dest);
|
||||
updateChecksum(dest);
|
||||
} finally {
|
||||
raf.close();
|
||||
dest.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSignature(FileChannel fileChannel) throws IOException {
|
||||
private void updateSignature(@Nonnull DexDataStore dataStore) throws IOException {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-1");
|
||||
@ -235,14 +236,12 @@ public abstract class DexWriter<
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
|
||||
fileChannel.position(HeaderItem.HEADER_SIZE_OFFSET);
|
||||
int bytesRead = fileChannel.read(buffer);
|
||||
byte[] buffer = new byte[4 * 1024];
|
||||
InputStream input = dataStore.readAt(HeaderItem.HEADER_SIZE_OFFSET);
|
||||
int bytesRead = input.read(buffer);
|
||||
while (bytesRead >= 0) {
|
||||
buffer.rewind();
|
||||
md.update(buffer);
|
||||
buffer.clear();
|
||||
bytesRead = fileChannel.read(buffer);
|
||||
md.update(buffer, 0, bytesRead);
|
||||
bytesRead = input.read(buffer);
|
||||
}
|
||||
|
||||
byte[] signature = md.digest();
|
||||
@ -251,38 +250,30 @@ public abstract class DexWriter<
|
||||
}
|
||||
|
||||
// write signature
|
||||
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
|
||||
fileChannel.write(ByteBuffer.wrap(signature));
|
||||
|
||||
// flush
|
||||
fileChannel.force(false);
|
||||
OutputStream output = dataStore.outputAt(HeaderItem.SIGNATURE_OFFSET);
|
||||
output.write(signature);
|
||||
output.close();
|
||||
}
|
||||
|
||||
private void updateChecksum(FileChannel fileChannel) throws IOException {
|
||||
private void updateChecksum(@Nonnull DexDataStore dataStore) throws IOException {
|
||||
Adler32 a32 = new Adler32();
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);
|
||||
fileChannel.position(HeaderItem.SIGNATURE_OFFSET);
|
||||
int bytesRead = fileChannel.read(buffer);
|
||||
byte[] buffer = new byte[4 * 1024];
|
||||
InputStream input = dataStore.readAt(HeaderItem.SIGNATURE_OFFSET);
|
||||
int bytesRead = input.read(buffer);
|
||||
while (bytesRead >= 0) {
|
||||
a32.update(buffer.array(), 0, bytesRead);
|
||||
buffer.clear();
|
||||
bytesRead = fileChannel.read(buffer);
|
||||
a32.update(buffer, 0, bytesRead);
|
||||
bytesRead = input.read(buffer);
|
||||
}
|
||||
|
||||
// write checksum, utilizing logic in DexWriter to write the integer value properly
|
||||
fileChannel.position(HeaderItem.CHECKSUM_OFFSET);
|
||||
int checksum = (int) a32.getValue();
|
||||
ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream();
|
||||
DexDataWriter.writeInt(checksumBuf, checksum);
|
||||
fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray()));
|
||||
|
||||
// flush
|
||||
fileChannel.force(false);
|
||||
OutputStream output = dataStore.outputAt(HeaderItem.CHECKSUM_OFFSET);
|
||||
DexDataWriter.writeInt(output, (int)a32.getValue());
|
||||
output.close();
|
||||
}
|
||||
|
||||
private static DexDataWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException {
|
||||
return new DexDataWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition);
|
||||
private static DexDataWriter outputAt(DexDataStore dataStore, int filePosition) throws IOException {
|
||||
return new DexDataWriter(dataStore.outputAt(filePosition), filePosition);
|
||||
}
|
||||
|
||||
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 {
|
||||
debugSectionOffset = writer.getPosition();
|
||||
DebugWriter<StringKey, TypeKey> debugWriter =
|
||||
new DebugWriter<StringKey, TypeKey>(stringSection, typeSection, writer);
|
||||
private static class CodeItemOffset<MethodKey> {
|
||||
@Nonnull MethodKey method;
|
||||
int codeOffset;
|
||||
|
||||
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 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 CodeItemOffset(@Nonnull MethodKey method, int codeOffset) {
|
||||
this.codeOffset = codeOffset;
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException {
|
||||
private void writeDebugAndCodeItems(@Nonnull DexDataWriter offsetWriter,
|
||||
@Nonnull DeferredOutputStream temp) throws IOException {
|
||||
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()) {
|
||||
Collection<? extends MethodKey> directMethods = classSection.getSortedDirectMethods(classKey);
|
||||
Collection<? extends MethodKey> virtualMethods = classSection.getSortedVirtualMethods(classKey);
|
||||
@ -790,223 +727,307 @@ public abstract class DexWriter<
|
||||
Iterable<MethodKey> methods = Iterables.concat(directMethods, virtualMethods);
|
||||
|
||||
for (MethodKey methodKey: methods) {
|
||||
Iterable<? extends Instruction> instructions = classSection.getInstructions(methodKey);
|
||||
int debugItemOffset = classSection.getDebugItemOffset(methodKey);
|
||||
int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, methodKey);
|
||||
int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, debugItemOffset);
|
||||
|
||||
if (instructions == null && debugItemOffset == NO_OFFSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
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 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() {
|
||||
|
@ -345,14 +345,6 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
|
||||
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) {
|
||||
if (stringReference == null) {
|
||||
return null;
|
||||
|
@ -49,7 +49,6 @@ public class BuilderMethod extends BaseMethodReference implements Method {
|
||||
|
||||
int annotationSetRefListOffset = DexWriter.NO_OFFSET;
|
||||
int codeItemOffset = DexWriter.NO_OFFSET;
|
||||
int debugInfoOffset = DexWriter.NO_OFFSET;
|
||||
|
||||
BuilderMethod(@Nonnull BuilderMethodReference methodReference,
|
||||
@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;
|
||||
}
|
||||
|
||||
@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,
|
||||
DebugItem debugItem) throws IOException {
|
||||
switch (debugItem.getDebugItemType()) {
|
||||
|
@ -40,10 +40,12 @@ import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.dexlib2.iface.value.*;
|
||||
import org.jf.dexlib2.immutable.instruction.ImmutableInstructionFactory;
|
||||
import org.jf.dexlib2.writer.DexWriter;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
import org.jf.dexlib2.writer.pool.ProtoPool.Key;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
@ -86,7 +88,7 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
|
||||
for (ClassDef classDef: input.getClasses()) {
|
||||
((ClassPool)dexPool.classSection).intern(classDef);
|
||||
}
|
||||
dexPool.writeTo(path);
|
||||
dexPool.writeTo(new FileDataStore(new File(path)));
|
||||
}
|
||||
|
||||
@Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer,
|
||||
|
@ -48,7 +48,6 @@ class PoolMethod extends BaseMethodReference implements Method {
|
||||
@Nonnull private final Method method;
|
||||
protected int annotationSetRefListOffset = 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>() {
|
||||
@Override public PoolMethod apply(Method method) {
|
||||
|
@ -36,6 +36,7 @@ import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
import org.jf.util.SmaliHelpFormatter;
|
||||
|
||||
@ -225,7 +226,7 @@ public class main {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
dexBuilder.writeTo(outputDexFile);
|
||||
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
ex.printStackTrace();
|
||||
@ -241,14 +242,14 @@ public class main {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for(File file: files) {
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
if (file.isDirectory()) {
|
||||
getSmaliFilesInDir(file, smaliFiles);
|
||||
} else if (file.getName().endsWith(".smali")) {
|
||||
smaliFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
|
||||
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