mirror of
https://github.com/revanced/smali.git
synced 2025-05-03 16:14:29 +02:00
Add support for writing code_items
This commit is contained in:
parent
56c7adde03
commit
f3c33259dd
@ -40,10 +40,7 @@ import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.util.InstructionOffsetMap;
|
||||
import org.jf.dexlib2.util.MethodUtil;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
import org.jf.dexlib2.util.TypeUtils;
|
||||
import org.jf.dexlib2.util.*;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
@ -103,7 +100,7 @@ public class MethodDefinition {
|
||||
}catch (Exception ex) {
|
||||
String methodString;
|
||||
try {
|
||||
methodString = MethodUtil.buildFullMethodString(classDef.classDef, method);
|
||||
methodString = ReferenceUtil.getMethodDescriptor(method);
|
||||
} catch (Exception ex2) {
|
||||
throw ExceptionWithContext.withContext(ex, "Error while processing method");
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import org.jf.dexlib2.iface.ExceptionHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Comparator;
|
||||
|
||||
public abstract class BaseExceptionHandler implements ExceptionHandler {
|
||||
@Override
|
||||
@ -65,9 +66,31 @@ public abstract class BaseExceptionHandler implements ExceptionHandler {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
String otherExceptionType = o.getExceptionType();
|
||||
if (otherExceptionType == null) {
|
||||
return -1;
|
||||
}
|
||||
res = exceptionType.compareTo(o.getExceptionType());
|
||||
if (res != 0) return res;
|
||||
}
|
||||
return Ints.compare(getHandlerCodeAddress(), o.getHandlerCodeAddress());
|
||||
}
|
||||
|
||||
public static final Comparator<ExceptionHandler> BY_EXCEPTION = new Comparator<ExceptionHandler>() {
|
||||
@Override public int compare(ExceptionHandler o1, ExceptionHandler o2) {
|
||||
String exceptionType1 = o1.getExceptionType();
|
||||
if (exceptionType1 == null) {
|
||||
if (o2.getExceptionType() != null) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
String exceptionType2 = o2.getExceptionType();
|
||||
if (exceptionType2 == null) {
|
||||
return -1;
|
||||
}
|
||||
return exceptionType1.compareTo(o2.getExceptionType());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -61,8 +61,8 @@ public interface TryBlock {
|
||||
/**
|
||||
* A list of the exception handlers associated with this try block.
|
||||
*
|
||||
* The exception handlers in the returned set will all have a unique type, including at most 1 with no type, which
|
||||
* is the catch-all handler.
|
||||
* The exception handlers in the returned list will all have a unique type, including at most 1 with no type, which
|
||||
* is the catch-all handler. Catch-all handler is always the last item in the list.
|
||||
*
|
||||
* @return A list of ExceptionHandler objects
|
||||
*/
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.util;
|
||||
|
||||
import org.jf.dexlib2.Opcode;
|
||||
|
||||
public final class InstructionUtil {
|
||||
public static boolean isInvokeStatic(Opcode opcode) {
|
||||
return opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE;
|
||||
}
|
||||
|
||||
private InstructionUtil() {}
|
||||
}
|
@ -32,31 +32,38 @@
|
||||
package org.jf.dexlib2.util;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.MethodParameter;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.iface.reference.TypeReference;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class MethodUtil {
|
||||
public static String buildFullMethodString(ClassDef classDef, Method method) {
|
||||
//TODO: consider using a cached thread-local StringBuilder
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(classDef.getType());
|
||||
sb.append("->");
|
||||
sb.append(method.getName());
|
||||
sb.append("(");
|
||||
for (MethodParameter methodParameter: method.getParameters()) {
|
||||
sb.append(methodParameter.getType());
|
||||
}
|
||||
sb.append(")");
|
||||
sb.append(method.getReturnType());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static int directMask = AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
|
||||
AccessFlags.CONSTRUCTOR.getValue();
|
||||
|
||||
public static boolean isDirect(Method method) {
|
||||
return (method.getAccessFlags() | directMask) != 0;
|
||||
public static boolean isDirect(@Nonnull Method method) {
|
||||
return (method.getAccessFlags() & directMask) != 0;
|
||||
}
|
||||
|
||||
public static boolean isStatic(@Nonnull Method method) {
|
||||
return AccessFlags.STATIC.isSet(method.getAccessFlags());
|
||||
}
|
||||
|
||||
public static int getParameterRegisterCount(@Nonnull MethodReference methodRef, boolean isStatic) {
|
||||
int regCount = 0;
|
||||
for (TypeReference param: methodRef.getParameters()) {
|
||||
int firstChar = param.getType().charAt(0);
|
||||
if (firstChar == 'J' || firstChar == 'D') {
|
||||
regCount += 2;
|
||||
} else {
|
||||
regCount++;
|
||||
}
|
||||
}
|
||||
if (!isStatic) {
|
||||
regCount++;
|
||||
}
|
||||
return regCount;
|
||||
}
|
||||
|
||||
private MethodUtil() {}
|
||||
|
552
dexlib2/src/main/java/org/jf/dexlib2/writer/CodeItemPool.java
Normal file
552
dexlib2/src/main/java/org/jf/dexlib2/writer/CodeItemPool.java
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* Copyright 2012, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.writer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.iface.ExceptionHandler;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.MethodImplementation;
|
||||
import org.jf.dexlib2.iface.TryBlock;
|
||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||
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.util.ReferenceUtil;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CodeItemPool {
|
||||
@Nonnull private final Map<Method, Integer> codeItemOffsetMap = Maps.newHashMap();
|
||||
@Nonnull private final DexFile dexFile;
|
||||
private int sectionOffset = -1;
|
||||
|
||||
public CodeItemPool(@Nonnull DexFile dexFile) {
|
||||
this.dexFile = dexFile;
|
||||
}
|
||||
|
||||
public void intern(@Nonnull Method method) {
|
||||
// TODO: can we have parameter names (in the debug_info_item), without having any other sort of method implementation
|
||||
// this also handles parameter names, which aren't directly tied to the MethodImplementation, even though the debug items are
|
||||
boolean hasDebugInfo = dexFile.debugInfoPool.intern(method);
|
||||
boolean hasInstruction = false;
|
||||
|
||||
MethodImplementation methodImpl = method.getImplementation();
|
||||
if (methodImpl != null) {
|
||||
for (Instruction instruction: methodImpl.getInstructions()) {
|
||||
hasInstruction = true;
|
||||
if (instruction instanceof ReferenceInstruction) {
|
||||
Reference reference = ((ReferenceInstruction)instruction).getReference();
|
||||
switch (instruction.getOpcode().referenceType) {
|
||||
case ReferenceType.STRING:
|
||||
dexFile.stringPool.intern((StringReference) reference);
|
||||
break;
|
||||
case ReferenceType.TYPE:
|
||||
dexFile.typePool.intern((TypeReference)reference);
|
||||
break;
|
||||
case ReferenceType.FIELD:
|
||||
dexFile.fieldPool.intern((FieldReference) reference);
|
||||
break;
|
||||
case ReferenceType.METHOD:
|
||||
dexFile.methodPool.intern((MethodReference)reference);
|
||||
break;
|
||||
default:
|
||||
throw new ExceptionWithContext("Unrecognized reference type: %d",
|
||||
instruction.getOpcode().referenceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<? extends TryBlock> tryBlocks = methodImpl.getTryBlocks();
|
||||
if (!hasInstruction && tryBlocks.size() > 0) {
|
||||
throw new ExceptionWithContext("Method %s has no instructions, but has try blocks.",
|
||||
ReferenceUtil.getMethodDescriptor(method));
|
||||
}
|
||||
|
||||
for (TryBlock tryBlock: methodImpl.getTryBlocks()) {
|
||||
for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
|
||||
dexFile.typePool.internNullable(handler.getExceptionType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDebugInfo || hasInstruction) {
|
||||
codeItemOffsetMap.put(method, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public int getOffset(@Nonnull Method method) {
|
||||
Integer offset = codeItemOffsetMap.get(method);
|
||||
if (offset == null) {
|
||||
return 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
public int getNumItems() {
|
||||
return codeItemOffsetMap.size();
|
||||
}
|
||||
|
||||
public int getSectionOffset() {
|
||||
if (sectionOffset < 0) {
|
||||
throw new ExceptionWithContext("Section offset has not been set yet!");
|
||||
}
|
||||
return sectionOffset;
|
||||
}
|
||||
|
||||
public void write(@Nonnull DexWriter writer) throws IOException {
|
||||
ByteArrayOutputStream ehBuf = new ByteArrayOutputStream();
|
||||
|
||||
writer.align();
|
||||
sectionOffset = writer.getPosition();
|
||||
List<Method> methods = Lists.newArrayList(codeItemOffsetMap.keySet());
|
||||
Collections.sort(methods);
|
||||
for (Method method: methods) {
|
||||
writer.align();
|
||||
codeItemOffsetMap.put(method, writer.getPosition());
|
||||
|
||||
MethodImplementation methodImpl = method.getImplementation();
|
||||
if (methodImpl != null) {
|
||||
writer.writeUshort(methodImpl.getRegisterCount());
|
||||
writer.writeUshort(MethodUtil.getParameterRegisterCount(method, MethodUtil.isStatic(method)));
|
||||
|
||||
int maxOutParamCount = 0;
|
||||
int codeUnitCount = 0;
|
||||
for (Instruction instruction: methodImpl.getInstructions()) {
|
||||
if (instruction.getOpcode().referenceType == ReferenceType.METHOD) {
|
||||
codeUnitCount += instruction.getCodeUnits();
|
||||
|
||||
ReferenceInstruction refInsn = (ReferenceInstruction)instruction;
|
||||
MethodReference methodRef = (MethodReference)refInsn.getReference();
|
||||
int paramCount = MethodUtil.getParameterRegisterCount(methodRef,
|
||||
InstructionUtil.isInvokeStatic(instruction.getOpcode()));
|
||||
if (paramCount > maxOutParamCount) {
|
||||
maxOutParamCount = paramCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.writeUshort(maxOutParamCount);
|
||||
|
||||
List<? extends TryBlock> tryBlocks = methodImpl.getTryBlocks();
|
||||
writer.writeUshort(tryBlocks.size());
|
||||
writer.writeInt(dexFile.debugInfoPool.getOffset(method));
|
||||
writer.writeInt(codeUnitCount);
|
||||
|
||||
// TODO: need to fix up instructions. Add alignment nops, convert to const-string/jumbos, etc.
|
||||
|
||||
for (Instruction instruction: methodImpl.getInstructions()) {
|
||||
switch (instruction.getOpcode().format) {
|
||||
case Format10t:
|
||||
writeFormat10t(writer, (Instruction10t)instruction);
|
||||
break;
|
||||
case Format10x:
|
||||
writeFormat10x(writer, (Instruction10x)instruction);
|
||||
break;
|
||||
case Format11n:
|
||||
writeFormat11n(writer, (Instruction11n)instruction);
|
||||
break;
|
||||
case Format11x:
|
||||
writeFormat11x(writer, (Instruction11x)instruction);
|
||||
break;
|
||||
case Format12x:
|
||||
writeFormat12x(writer, (Instruction12x)instruction);
|
||||
break;
|
||||
case Format20t:
|
||||
writeFormat20t(writer, (Instruction20t)instruction);
|
||||
break;
|
||||
case Format21c:
|
||||
writeFormat21c(writer, (Instruction21c)instruction);
|
||||
break;
|
||||
case Format21ih:
|
||||
writeFormat21ih(writer, (Instruction21ih)instruction);
|
||||
break;
|
||||
case Format21lh:
|
||||
writeFormat21lh(writer, (Instruction21lh)instruction);
|
||||
break;
|
||||
case Format21s:
|
||||
writeFormat21s(writer, (Instruction21s)instruction);
|
||||
break;
|
||||
case Format21t:
|
||||
writeFormat21t(writer, (Instruction21t)instruction);
|
||||
break;
|
||||
case Format22b:
|
||||
writeFormat22b(writer, (Instruction22b)instruction);
|
||||
break;
|
||||
case Format22c:
|
||||
writeFormat22c(writer, (Instruction22c)instruction);
|
||||
break;
|
||||
case Format22s:
|
||||
writeFormat22s(writer, (Instruction22s)instruction);
|
||||
break;
|
||||
case Format22t:
|
||||
writeFormat22t(writer, (Instruction22t)instruction);
|
||||
break;
|
||||
case Format22x:
|
||||
writeFormat22x(writer, (Instruction22x)instruction);
|
||||
break;
|
||||
case Format23x:
|
||||
writeFormat23x(writer, (Instruction23x)instruction);
|
||||
break;
|
||||
case Format30t:
|
||||
writeFormat30t(writer, (Instruction30t)instruction);
|
||||
break;
|
||||
case Format31c:
|
||||
writeFormat31c(writer, (Instruction31c)instruction);
|
||||
break;
|
||||
case Format31i:
|
||||
writeFormat31i(writer, (Instruction31i)instruction);
|
||||
break;
|
||||
case Format31t:
|
||||
writeFormat31t(writer, (Instruction31t)instruction);
|
||||
break;
|
||||
case Format32x:
|
||||
writeFormat32x(writer, (Instruction32x)instruction);
|
||||
break;
|
||||
case Format35c:
|
||||
writeFormat35c(writer, (Instruction35c)instruction);
|
||||
break;
|
||||
case Format3rc:
|
||||
writeFormat3rc(writer, (Instruction3rc)instruction);
|
||||
break;
|
||||
case Format51l:
|
||||
writeFormat51l(writer, (Instruction51l)instruction);
|
||||
break;
|
||||
case ArrayPayload:
|
||||
writeArrayPayload(writer, (ArrayPayload)instruction);
|
||||
break;
|
||||
case SparseSwitchPayload:
|
||||
writeSparseSwitchPayload(writer, (SparseSwitchPayload)instruction);
|
||||
break;
|
||||
case PackedSwitchPayload:
|
||||
writePackedSwitchPayload(writer, (PackedSwitchPayload)instruction);
|
||||
break;
|
||||
default:
|
||||
throw new ExceptionWithContext("Unexpected 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 tryBlock: tryBlocks) {
|
||||
exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0);
|
||||
}
|
||||
DexWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size());
|
||||
|
||||
for (TryBlock tryBlock: tryBlocks) {
|
||||
writer.writeInt(tryBlock.getStartCodeAddress());
|
||||
writer.writeUshort(tryBlock.getCodeUnitCount());
|
||||
|
||||
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
|
||||
DexWriter.writeSleb128(ehBuf, ehSize);
|
||||
for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) {
|
||||
String exceptionType = eh.getExceptionType();
|
||||
int codeAddress = eh.getHandlerCodeAddress();
|
||||
|
||||
if (exceptionType != null) {
|
||||
//regular exception handling
|
||||
DexWriter.writeUleb128(ehBuf, dexFile.typePool.getIndex(exceptionType));
|
||||
DexWriter.writeUleb128(ehBuf, codeAddress);
|
||||
} else {
|
||||
//catch-all
|
||||
DexWriter.writeUleb128(ehBuf, codeAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ehBuf.size() > 0) {
|
||||
ehBuf.writeTo(writer);
|
||||
ehBuf.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int packNibbles(int a, int b) {
|
||||
return (b << 4) | a;
|
||||
}
|
||||
|
||||
private int getReferenceIndex(ReferenceInstruction referenceInstruction) {
|
||||
switch (referenceInstruction.getOpcode().referenceType) {
|
||||
case ReferenceType.FIELD:
|
||||
return dexFile.fieldPool.getIndex((FieldReference)referenceInstruction.getReference());
|
||||
case ReferenceType.METHOD:
|
||||
return dexFile.methodPool.getIndex((MethodReference)referenceInstruction.getReference());
|
||||
case ReferenceType.STRING:
|
||||
return dexFile.stringPool.getIndex((StringReference)referenceInstruction.getReference());
|
||||
case ReferenceType.TYPE:
|
||||
return dexFile.typePool.getIndex((TypeReference)referenceInstruction.getReference());
|
||||
default:
|
||||
throw new ExceptionWithContext("Unknown reference type: %d",
|
||||
referenceInstruction.getOpcode().referenceType);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeFormat10t(@Nonnull DexWriter writer, @Nonnull Instruction10t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat10x(@Nonnull DexWriter writer, @Nonnull Instruction10x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(0);
|
||||
}
|
||||
|
||||
public void writeFormat11n(@Nonnull DexWriter writer, @Nonnull Instruction11n instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral()));
|
||||
}
|
||||
|
||||
public void writeFormat11x(@Nonnull DexWriter writer, @Nonnull Instruction11x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
}
|
||||
|
||||
public void writeFormat12x(@Nonnull DexWriter writer, @Nonnull Instruction12x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
}
|
||||
|
||||
public void writeFormat20t(@Nonnull DexWriter writer, @Nonnull Instruction20t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(0);
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat21c(@Nonnull DexWriter writer, @Nonnull Instruction21c instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
}
|
||||
|
||||
public void writeFormat21ih(@Nonnull DexWriter writer, @Nonnull Instruction21ih instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getHatLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat21lh(@Nonnull DexWriter writer, @Nonnull Instruction21lh instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getHatLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat21s(@Nonnull DexWriter writer, @Nonnull Instruction21s instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getNarrowLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat21t(@Nonnull DexWriter writer, @Nonnull Instruction21t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat22b(@Nonnull DexWriter writer, @Nonnull Instruction22b instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.write(instruction.getRegisterB());
|
||||
writer.write(instruction.getNarrowLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat22c(@Nonnull DexWriter writer, @Nonnull Instruction22c instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
}
|
||||
|
||||
public void writeFormat22s(@Nonnull DexWriter writer, @Nonnull Instruction22s instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeShort(instruction.getNarrowLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat22t(@Nonnull DexWriter writer, @Nonnull Instruction22t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat22x(@Nonnull DexWriter writer, @Nonnull Instruction22x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeUshort(instruction.getRegisterB());
|
||||
}
|
||||
|
||||
public void writeFormat23x(@Nonnull DexWriter writer, @Nonnull Instruction23x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.write(instruction.getRegisterB());
|
||||
writer.write(instruction.getRegisterC());
|
||||
}
|
||||
|
||||
public void writeFormat30t(@Nonnull DexWriter writer, @Nonnull Instruction30t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(0);
|
||||
writer.writeInt(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat31c(@Nonnull DexWriter writer, @Nonnull Instruction31c instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(getReferenceIndex(instruction));
|
||||
}
|
||||
|
||||
public void writeFormat31i(@Nonnull DexWriter writer, @Nonnull Instruction31i instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(instruction.getNarrowLiteral());
|
||||
}
|
||||
|
||||
public void writeFormat31t(@Nonnull DexWriter writer, @Nonnull Instruction31t instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(instruction.getCodeOffset());
|
||||
}
|
||||
|
||||
public void writeFormat32x(@Nonnull DexWriter writer, @Nonnull Instruction32x instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(0);
|
||||
writer.writeUshort(instruction.getRegisterA());
|
||||
writer.writeUshort(instruction.getRegisterB());
|
||||
}
|
||||
|
||||
public void writeFormat35c(@Nonnull DexWriter writer, @Nonnull Instruction35c instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
|
||||
writer.write(getReferenceIndex(instruction));
|
||||
writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
|
||||
writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF()));
|
||||
}
|
||||
|
||||
public void writeFormat3rc(@Nonnull DexWriter writer, @Nonnull Instruction3rc instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterCount());
|
||||
writer.write(getReferenceIndex(instruction));
|
||||
writer.writeUshort(instruction.getStartRegister());
|
||||
}
|
||||
|
||||
public void writeFormat51l(@Nonnull DexWriter writer, @Nonnull Instruction51l instruction) throws IOException {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeLong(instruction.getWideLiteral());
|
||||
}
|
||||
|
||||
public void writeArrayPayload(@Nonnull DexWriter writer, @Nonnull ArrayPayload instruction) throws IOException {
|
||||
writer.writeUshort(instruction.getOpcode().value);
|
||||
writer.writeUshort(instruction.getElementWidth());
|
||||
List<Number> elements = instruction.getArrayElements();
|
||||
writer.writeInt(elements.size());
|
||||
// TODO: validate that dalvik only allows these element widths
|
||||
switch (instruction.getElementWidth()) {
|
||||
case 1:
|
||||
for (Number element: elements) {
|
||||
writer.write(element.byteValue());
|
||||
}
|
||||
return;
|
||||
case 2:
|
||||
for (Number element: elements) {
|
||||
writer.writeShort(element.shortValue());
|
||||
}
|
||||
return;
|
||||
case 4:
|
||||
for (Number element: elements) {
|
||||
writer.writeInt(element.intValue());
|
||||
}
|
||||
return;
|
||||
case 8:
|
||||
for (Number element: elements) {
|
||||
writer.writeLong(element.longValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeSparseSwitchPayload(@Nonnull DexWriter writer, @Nonnull SparseSwitchPayload instruction)
|
||||
throws IOException {
|
||||
writer.writeUshort(instruction.getOpcode().value);
|
||||
List<? extends SwitchElement> elements = instruction.getSwitchElements();
|
||||
writer.writeUshort(elements.size());
|
||||
for (SwitchElement element: elements) {
|
||||
writer.writeInt(element.getKey());
|
||||
}
|
||||
for (SwitchElement element: elements) {
|
||||
writer.writeInt(element.getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
public void writePackedSwitchPayload(@Nonnull DexWriter writer, @Nonnull PackedSwitchPayload instruction)
|
||||
throws IOException {
|
||||
writer.writeUshort(instruction.getOpcode().value);
|
||||
List<? extends SwitchElement> elements = instruction.getSwitchElements();
|
||||
writer.writeUshort(elements.size());
|
||||
writer.writeInt(elements.get(0).getKey());
|
||||
for (SwitchElement element: elements) {
|
||||
writer.writeInt(element.getOffset());
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ public class DebugInfoPool {
|
||||
this.dexFile = dexFile;
|
||||
}
|
||||
|
||||
public void intern(@Nonnull Method method) {
|
||||
public boolean intern(@Nonnull Method method) {
|
||||
boolean hasDebugInfo = false;
|
||||
for (MethodParameter param: method.getParameters()) {
|
||||
String paramName = param.getName();
|
||||
@ -85,6 +85,7 @@ public class DebugInfoPool {
|
||||
if (hasDebugInfo) {
|
||||
debugInfoOffsetMap.put(method, 0);
|
||||
}
|
||||
return hasDebugInfo;
|
||||
}
|
||||
|
||||
public int getOffset(@Nonnull Method method) {
|
||||
|
@ -67,6 +67,7 @@ public class DexFile {
|
||||
@Nonnull final AnnotationSetRefPool annotationSetRefPool = new AnnotationSetRefPool(this);
|
||||
@Nonnull final AnnotationDirectoryPool annotationDirectoryPool = new AnnotationDirectoryPool(this);
|
||||
@Nonnull final DebugInfoPool debugInfoPool = new DebugInfoPool(this);
|
||||
@Nonnull final CodeItemPool codeItemPool = new CodeItemPool(this);
|
||||
|
||||
@Nonnull private final Set<? extends ClassDef> classes;
|
||||
|
||||
@ -197,10 +198,7 @@ public class DexFile {
|
||||
public void internMethods(@Nonnull ClassDef classDef) {
|
||||
for (Method method: classDef.getMethods()) {
|
||||
methodPool.intern(method);
|
||||
// TODO: can we have parameter names (in the debug_info_item), without having any other sort of method implementation
|
||||
|
||||
// this also handles parameter names, which aren't directly tied to the MethodImplementation, even though the debug items are
|
||||
debugInfoPool.intern(method);
|
||||
codeItemPool.intern(method);
|
||||
|
||||
MethodImplementation methodImpl = method.getImplementation();
|
||||
if (methodImpl != null) {
|
||||
@ -293,6 +291,7 @@ public class DexFile {
|
||||
annotationSetRefPool.write(offsetWriter);
|
||||
annotationDirectoryPool.write(offsetWriter);
|
||||
debugInfoPool.write(offsetWriter);
|
||||
codeItemPool.write(offsetWriter);
|
||||
} finally {
|
||||
indexWriter.close();
|
||||
offsetWriter.close();
|
||||
|
@ -112,6 +112,11 @@ public class DexWriter extends OutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLong(long value) throws IOException {
|
||||
writeInt((int)value);
|
||||
writeInt((int)(value >> 32));
|
||||
}
|
||||
|
||||
public void writeInt(int value) throws IOException {
|
||||
write(value);
|
||||
write(value >> 8);
|
||||
@ -142,66 +147,37 @@ public class DexWriter extends OutputStream {
|
||||
write(value);
|
||||
}
|
||||
|
||||
public void writeUleb128(int value) throws IOException {
|
||||
public static void writeUleb128(OutputStream out, int value) throws IOException {
|
||||
while (value > 0x7f) {
|
||||
write((value & 0x7f) | 0x80);
|
||||
out.write((value & 0x7f) | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
write(value);
|
||||
out.write(value);
|
||||
}
|
||||
|
||||
public void writeUleb128(int value) throws IOException {
|
||||
writeUleb128(this, value);
|
||||
}
|
||||
|
||||
public static void writeSleb128(OutputStream out, int value) throws IOException {
|
||||
if (value >= 0) {
|
||||
while (value > 0x3f) {
|
||||
out.write((value & 0x7f) | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
out.write(value & 0x7f);
|
||||
} else {
|
||||
while (value < -0x40) {
|
||||
out.write((value & 0x7f) | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
out.write(value & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeSleb128(int value) throws IOException {
|
||||
if (value >= 0) {
|
||||
while (value > 0x3f) {
|
||||
write((value & 0x7f) | 0x80);
|
||||
value >>>= 7;
|
||||
writeSleb128(this, value);
|
||||
}
|
||||
write(value & 0x7f);
|
||||
} else {
|
||||
while (value < -0x40) {
|
||||
write((value & 0x7f) | 0x80);
|
||||
value >>= 7;
|
||||
}
|
||||
write(value & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
/* public static byte[] encodeSignedIntegralValue(long value) {
|
||||
int requiredBytes = getRequiredBytesForSignedIntegralValue(value);
|
||||
|
||||
byte[] bytes = new byte[requiredBytes];
|
||||
|
||||
for (int i = 0; i < requiredBytes; i++) {
|
||||
bytes[i] = (byte) value;
|
||||
value >>= 8;
|
||||
}
|
||||
return bytes;
|
||||
}*/
|
||||
|
||||
|
||||
/*
|
||||
public static long decodeUnsignedIntegralValue(byte[] bytes) {
|
||||
long value = 0;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
value |= (((long)(bytes[i] & 0xFF)) << i * 8);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public static byte[] encodeUnsignedIntegralValue(long value) {
|
||||
int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value);
|
||||
|
||||
byte[] bytes = new byte[requiredBytes];
|
||||
|
||||
for (int i = 0; i < requiredBytes; i++) {
|
||||
bytes[i] = (byte) value;
|
||||
value >>= 8;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
*/
|
||||
|
||||
public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException {
|
||||
write(valueType | (valueArg << 5));
|
||||
|
Loading…
x
Reference in New Issue
Block a user