Merge pull request #592 from pxb1988/dex040

* support dex039 & dex040

support read/write/smali/baksmali dex039.

support const-mothod-type and const-method-handler, syntax is same as google's smali fork. (google 's smali 3.0.3 can't assemble the two instruction back to dex)

dex040 support space and other unicode in SimpleName, d2j-smali will escape space char to unicode.
This commit is contained in:
Bob Pan 2023-08-31 13:07:19 +08:00 committed by GitHub
commit caf241d635
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 271 additions and 61 deletions

View File

@ -21,6 +21,7 @@ subprojects {
artifacts.archives packageSources
repositories {
mavenCentral()
google()
}
// == support provided scope

View File

@ -6,7 +6,7 @@ dependencies {
antlr 'org.antlr:antlr4:4.5'
compile project(':d2j-base-cmd')
compile project(':dex-writer')
testCompile 'org.smali:baksmali:2.0.6'
testImplementation "com.android.tools.smali:smali-baksmali:3.0.3"
}
generateGrammarSource {

View File

@ -58,7 +58,7 @@ fragment
FRAGMENT_ARRAY_TYPE: ('[')+ (FRAGMENT_PRIMITIVE_TYPE|FRAGMENT_OBJECT_TYPE);
fragment
FRAGMENT_ID: (ESC_SEQ| ~('\\'|'\r'|'\n'|'\t'|' '|':'|'-'|'='|','|'{'|'}'|'('|')'|'+'|'\"'|'\''|'#'|'/'|'.'|';'))+;
FRAGMENT_ID: (ESC_SEQ| ~('\\'|'\r'|'\n'|'\t'|' '|':'|'-'|'='|','|'{'|'}'|'('|')'|'+'|'\"'|'\''|'#'|'/'|'.'|';'|'@'))+;
fragment
FRAGMENT_METHOD_PROTO: '(' (FRAGMENT_OBJECT_TYPE|FRAGMENT_ARRAY_TYPE|FRAGMENT_PRIMITIVE_TYPE)* ')' ('V' | FRAGMENT_OBJECT_TYPE|FRAGMENT_ARRAY_TYPE|FRAGMENT_PRIMITIVE_TYPE)
;
@ -194,6 +194,10 @@ sBaseValue
;
sArrayValue: '{' sAnnotationValue? (',' sAnnotationValue)* '}';
method_handler
: type=('static-get'|'static-put'|'instance-get'|'instance-put') '@' fld=FIELD_FULL
| type=('invoke-static'|'invoke-instance'|'invoke-direct'|'invoke-interface'|'invoke-constructor') '@' mtd=METHOD_FULL
;
sInstruction
:fline
@ -269,6 +273,8 @@ fconst
r1=REGISTER ',' cst=(INT|LONG)
| op=('const-string'|'const-string/jumbo') r1=REGISTER ',' cst=STRING
| op=('const-class'|'check-cast'|'new-instance') r1=REGISTER ',' cst=(OBJECT_TYPE|ARRAY_TYPE)
| op='const-method-type' r1=REGISTER ',' cst=METHOD_PROTO
| op='const-method-handle' r1=REGISTER ',' h=method_handler
;
ff1c : op=(SGET
|'sget-wide'

View File

@ -352,6 +352,12 @@ public class AntlrSmaliUtil {
scv.visitConstStmt(op, r, v);
}
break;
case CONST_METHOD_HANDLE:
scv.visitConstStmt(op, r, parseMethodHandler(ctx.h));
break;
case CONST_METHOD_TYPE:
scv.visitConstStmt(op, r, parseProtoAndUnescape(ctx.cst.getText()));
break;
default:
throw new RuntimeException();
}
@ -548,6 +554,42 @@ public class AntlrSmaliUtil {
scv.visitEnd();
}
private static MethodHandle parseMethodHandler(SmaliParser.Method_handlerContext methodHandlerContext) {
MethodHandle value;
switch (methodHandlerContext.type.getText()) {
case "static-get":
value = new MethodHandle(MethodHandle.STATIC_GET, parseFieldAndUnescape(methodHandlerContext.fld.getText()));
break;
case "static-put":
value = new MethodHandle(MethodHandle.STATIC_PUT, parseFieldAndUnescape(methodHandlerContext.fld.getText()));
break;
case "instance-get":
value = new MethodHandle(MethodHandle.INSTANCE_GET, parseFieldAndUnescape(methodHandlerContext.fld.getText()));
break;
case "instance-put":
value = new MethodHandle(MethodHandle.INSTANCE_PUT, parseFieldAndUnescape(methodHandlerContext.fld.getText()));
break;
case "invoke-static":
value = new MethodHandle(MethodHandle.INVOKE_STATIC, parseMethodAndUnescape(methodHandlerContext.mtd.getText()));
break;
case "invoke-instance":
value = new MethodHandle(MethodHandle.INVOKE_INSTANCE, parseMethodAndUnescape(methodHandlerContext.mtd.getText()));
break;
case "invoke-direct":
value = new MethodHandle(MethodHandle.INVOKE_DIRECT, parseMethodAndUnescape(methodHandlerContext.mtd.getText()));
break;
case "invoke-interface":
value = new MethodHandle(MethodHandle.INVOKE_INTERFACE, parseMethodAndUnescape(methodHandlerContext.mtd.getText()));
break;
case "invoke-constructor":
value = new MethodHandle(MethodHandle.INVOKE_CONSTRUCTOR, parseMethodAndUnescape(methodHandlerContext.mtd.getText()));
break;
default:
throw new RuntimeException("not support yet: " + methodHandlerContext.type);
}
return value;
}
private static int findTotalRegisters(SmaliParser.SMethodContext ctx, int ins) {
int totalRegisters = -1;
List<SmaliParser.SInstructionContext> instructionContexts = ctx.sInstruction();

View File

@ -278,24 +278,24 @@ public class BaksmaliDumper implements DexConstants {
private static String escapeMethodHandle(MethodHandle obj) {
switch (obj.getType()) {
case MethodHandle.INSTANCE_GET:
return ".iget " + escapeField(obj.getField());
return "instance-get@" + escapeField(obj.getField());
case MethodHandle.INSTANCE_PUT:
return ".iput " + escapeField(obj.getField());
return "instance-put@" + escapeField(obj.getField());
case MethodHandle.STATIC_GET:
return ".sget " + escapeField(obj.getField());
return "static-get@" + escapeField(obj.getField());
case MethodHandle.STATIC_PUT:
return ".sput " + escapeField(obj.getField());
return "static-put@" + escapeField(obj.getField());
case MethodHandle.INVOKE_INSTANCE:
return ".invoke-instance " + escapeMethod(obj.getMethod());
return "invoke-instance@" + escapeMethod(obj.getMethod());
case MethodHandle.INVOKE_STATIC:
return ".invoke-static " + escapeMethod(obj.getMethod());
return "invoke-static@" + escapeMethod(obj.getMethod());
case MethodHandle.INVOKE_CONSTRUCTOR:
return ".invoke-constructor " + escapeMethod(obj.getMethod());
return "invoke-constructor@" + escapeMethod(obj.getMethod());
case MethodHandle.INVOKE_DIRECT:
return ".invoke-direct " + escapeMethod(obj.getMethod());
return "invoke-direct@" + escapeMethod(obj.getMethod());
case MethodHandle.INVOKE_INTERFACE:
return ".invoke-interface " + escapeMethod(obj.getMethod());
return "invoke-interface@" + escapeMethod(obj.getMethod());
default:
}
return "?";

View File

@ -3,6 +3,7 @@ package com.googlecode.d2j.smali;
import com.googlecode.d2j.DexConstants;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.Visibility;
import com.googlecode.d2j.reader.Op;
import com.googlecode.d2j.visitors.DexAnnotationVisitor;
@ -477,23 +478,30 @@ public class Utils implements DexConstants {
return -1;
}
public static Method parseMethodAndUnescape(String owner, String part) throws RuntimeException {
int x = part.indexOf('(');
if (x < 0) {
throw new RuntimeException();
}
public static Proto parseProtoAndUnescape(String part) throws RuntimeException {
int x = 0;
int y = part.indexOf(')', x);
if (y < 0) {
throw new RuntimeException();
}
String methodName = unEscapeId(part.substring(0, x));
String[] params = toTypeList(part.substring(x + 1, y));
for (int i = 0; i < params.length; i++) {
params[i] = unEscapeId(params[i]);
}
String ret = unEscapeId(part.substring(y + 1));
return new Method(owner, methodName, params, ret);
return new Proto(params, ret);
}
public static Method parseMethodAndUnescape(String owner, String part) throws RuntimeException {
int x = part.indexOf('(');
if (x < 0) {
throw new RuntimeException();
}
String methodName = unEscapeId(part.substring(0, x));
return new Method(owner, methodName, parseProtoAndUnescape(part.substring(x)));
}
public static Method parseMethodAndUnescape(String full) throws RuntimeException {

View File

@ -1,5 +1,14 @@
package a;
import com.android.tools.smali.baksmali.Adaptors.ClassDefinition;
import com.android.tools.smali.baksmali.BaksmaliOptions;
import com.android.tools.smali.baksmali.formatter.BaksmaliWriter;
import com.android.tools.smali.dexlib2.DexFileFactory;
import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedClassDef;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile;
import com.android.tools.smali.dexlib2.util.SyntheticAccessorResolver;
import com.googlecode.d2j.DexConstants;
import com.googlecode.d2j.dex.writer.DexFileWriter;
import com.googlecode.d2j.node.DexClassNode;
import com.googlecode.d2j.node.DexFileNode;
@ -8,18 +17,16 @@ import com.googlecode.d2j.reader.zip.ZipUtil;
import com.googlecode.d2j.smali.BaksmaliDumpOut;
import com.googlecode.d2j.smali.BaksmaliDumper;
import com.googlecode.d2j.smali.Smali;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import org.jf.util.IndentingWriter;
import org.junit.Assert;
import org.junit.Test;
import java.io.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -64,9 +71,18 @@ public class SmaliTest {
}
private void dotest(File dexFile) throws IOException {
int dexVersion = new DexFileReader(dexFile).getDexVersion();
Opcodes opcodes;
if (dexVersion >= DexConstants.DEX_039) {
opcodes = Opcodes.forApi(28);
} else if (dexVersion >= DexConstants.DEX_037) {
opcodes = Opcodes.forApi(26);
} else {
opcodes = Opcodes.forApi(0);
}
DexBackedDexFile dex;
try {
dex = DexFileFactory.loadDexFile(dexFile, 14, false);
dex = DexFileFactory.loadDexFile(dexFile, opcodes);
} catch (DexBackedDexFile.NotADexFile ex) {
ex.printStackTrace();
return;
@ -84,7 +100,7 @@ public class SmaliTest {
{
byte[] data = toDex(dexClassNode);
DexBackedClassDef def2 = new DexBackedDexFile(new Opcodes(14, false), data).getClasses().iterator().next();
DexBackedClassDef def2 = new DexBackedDexFile(opcodes, data).getClasses().iterator().next();
String baksmali3 = baksmali(def2); // original
Assert.assertEquals(smali, baksmali3);
}
@ -95,7 +111,7 @@ public class SmaliTest {
{
byte[] data = toDex(dexClassNode2);
DexBackedClassDef def2 = new DexBackedDexFile(new Opcodes(14, false), data).getClasses().iterator().next();
DexBackedClassDef def2 = new DexBackedDexFile(opcodes, data).getClasses().iterator().next();
String baksmali3 = baksmali(def2); // original
Assert.assertEquals(smali, baksmali3);
}
@ -119,13 +135,13 @@ public class SmaliTest {
}
private static String baksmali(DexBackedClassDef def) throws IOException {
baksmaliOptions opts = new baksmaliOptions();
opts.outputDebugInfo = false;
opts.syntheticAccessorResolver = new SyntheticAccessorResolver(Collections.EMPTY_LIST);
BaksmaliOptions opts = new BaksmaliOptions();
opts.debugInfo = false;
opts.syntheticAccessorResolver = new SyntheticAccessorResolver(def.dexFile.getOpcodes(), Collections.EMPTY_LIST);
ClassDefinition classDefinition = new ClassDefinition(opts, def);
StringWriter bufWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(bufWriter);
classDefinition.writeTo((IndentingWriter) writer);
BaksmaliWriter writer = new BaksmaliWriter(bufWriter);
classDefinition.writeTo(writer);
writer.flush();
return bufWriter.toString();
}

View File

@ -75,7 +75,12 @@ public final class Exprs {
public static Constant nString(String i) {
return new Constant(i);
}
public static Constant nMethodHandle(MethodHandle i) {
return new Constant(i);
}
public static Constant nProto(Proto i) {
return new Constant(i);
}
public static BinopExpr nAdd(Value a, Value b, String type) {
return new BinopExpr(VT.ADD, a, b, type);
}

View File

@ -56,4 +56,6 @@ public abstract interface DexConstants {
int DEX_036 = 0x00303336;
int DEX_037 = 0x00303337;
int DEX_038 = 0x00303338;
int DEX_039 = 0x00303339;
int DEX_040 = 0x00303340;
}

View File

@ -46,6 +46,8 @@ public class DvmFrame<V> {
case CONST_STRING:
case CONST_STRING_JUMBO:
case CONST_CLASS:
case CONST_METHOD_HANDLE:
case CONST_METHOD_TYPE:
setReg(((ConstStmtNode) insn).a, interpreter.newOperation(insn));
setTmp(null);
break;
@ -378,7 +380,7 @@ public class DvmFrame<V> {
setTmp(null);
break;
default:
throw new RuntimeException();
throw new RuntimeException("not support " + insn.op);
}
}

View File

@ -13,4 +13,6 @@ package com.googlecode.d2j.reader;
kIndexFieldOffset, // field offset (for static linked fields)
kIndexMethodAndProtoRef, // 038,
kIndexCallSiteRef, // 038,
kIndexMethodHandleRef, // 039, constant method handle reference index
kIndexProtoRef, // 039, prototype reference index
};

View File

@ -254,6 +254,10 @@ public enum Op implements CFG {
| kInstrInvoke, true), //
INVOKE_CUSTOM_RANGE(0xfd, "invoke-custom/range", kFmt3rc, kIndexCallSiteRef, kInstrCanContinue | kInstrCanThrow
| kInstrInvoke, true), //
CONST_METHOD_HANDLE(0xfe, "const-method-handle", kFmt21c, kIndexMethodHandleRef, kInstrCanContinue | kInstrCanThrow
, true), //
CONST_METHOD_TYPE(0xff, "const-method-type", kFmt21c, kIndexProtoRef, kInstrCanContinue | kInstrCanThrow
, true), //
BAD_OP(-1, "bad-opcode", null, kIndexNone, 0, false), //
;
public int opcode;

View File

@ -104,14 +104,23 @@ public class DexCodeVisitor {
}
/**
* <pre>
* CONST * CONST_WIDE * CONST_STRING * CONST_CLASS *
* </pre>
*
* @param op
* @param ra
* @see Op#CONST
* @see Op#CONST_4
* @see Op#CONST_16
* @see Op#CONST_HIGH16
* @see Op#CONST_WIDE
* @see Op#CONST_WIDE_16
* @see Op#CONST_WIDE_32
* @see Op#CONST_WIDE_HIGH16
* @see Op#CONST_STRING
* @see Op#CONST_STRING_JUMBO
* @see Op#CONST_CLASS
* @see Op#CONST_METHOD_HANDLE
* @see Op#CONST_METHOD_TYPE
* @param op CONST*
* @param ra register
* @param value
* int/long/type
* Integer,Long,DexType,MethodHandle,Proto
*/
public void visitConstStmt(Op op, int ra, Object value) {
if (visitor != null) {

View File

@ -164,8 +164,8 @@ public class DexFileReader implements BaseDexFileReader {
throw new DexException("not support magic.");
}
int version = in.getInt() >> 8;
if (version < 0 || version < DEX_035) {
throw new DexException("not support version.");
if (version < DEX_035 || version > DEX_040) {
System.err.println("Unknown DEX version. Trying anyway...");
}
this.dex_version = version;
in.order(ByteOrder.LITTLE_ENDIAN);
@ -1589,6 +1589,12 @@ public class DexFileReader implements BaseDexFileReader {
dcv.visitTypeStmt(op, a, -1, getType(b));
}
break;
case kIndexMethodHandleRef:
dcv.visitConstStmt(op, a, getMethodHandle(b));
break;
case kIndexProtoRef:
dcv.visitConstStmt(op, a, getProto(b));
break;
default:
break;
}

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,8 @@ import com.googlecode.d2j.DexLabel;
import com.googlecode.d2j.DexType;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.node.DexCodeNode;
import com.googlecode.d2j.node.TryCatchNode;
import com.googlecode.d2j.node.analysis.DvmFrame;
@ -560,6 +562,10 @@ public class Dex2IRConverter {
case CONST_STRING:
case CONST_STRING_JUMBO:
return b(nString((String) ((ConstStmtNode) insn).value));
case CONST_METHOD_HANDLE:
return b(nMethodHandle((MethodHandle) ((ConstStmtNode) insn).value));
case CONST_METHOD_TYPE:
return b(nProto((Proto) ((ConstStmtNode) insn).value));
case SGET:
case SGET_BOOLEAN:
case SGET_BYTE:

View File

@ -18,6 +18,7 @@ package com.googlecode.d2j.converter;
import com.googlecode.d2j.DexType;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.asm.LdcOptimizeAdapter;
import com.googlecode.d2j.dex.Dex2Asm;
@ -679,8 +680,8 @@ public class IR2JConverter implements Opcodes {
Constant cst = (Constant) value;
if (cst.value.equals(Constant.Null)) {
asm.visitInsn(ACONST_NULL);
} else if (cst.value instanceof DexType) {
asm.visitLdcInsn(Type.getType(((DexType) cst.value).desc));
} else if (cst.value instanceof DexType || cst.value instanceof MethodHandle || cst.value instanceof Proto) {
asm.visitLdcInsn(Dex2Asm.convertConstantValue(cst.value));
} else {
asm.visitLdcInsn(cst.value);
}

View File

@ -602,31 +602,31 @@ public class Dex2Asm {
MethodHandle mh = (MethodHandle) ele;
switch (mh.getType()) {
case MethodHandle.INSTANCE_GET:
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType(), false);
break;
case MethodHandle.INSTANCE_PUT:
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType(), false);
break;
case MethodHandle.STATIC_GET:
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
h = new Handle(Opcodes.H_GETSTATIC, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType(), false);
break;
case MethodHandle.STATIC_PUT:
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
h = new Handle(Opcodes.H_PUTSTATIC, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType(), false);
break;
case MethodHandle.INVOKE_INSTANCE:
h = new Handle(Opcodes.H_INVOKEVIRTUAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
h = new Handle(Opcodes.H_INVOKEVIRTUAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc(), false);
break;
case MethodHandle.INVOKE_STATIC:
h = new Handle(Opcodes.H_INVOKESTATIC, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
h = new Handle(Opcodes.H_INVOKESTATIC, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc(), false);
break;
case MethodHandle.INVOKE_CONSTRUCTOR:
h = new Handle(Opcodes.H_NEWINVOKESPECIAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
h = new Handle(Opcodes.H_NEWINVOKESPECIAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc(), false);
break;
case MethodHandle.INVOKE_DIRECT:
h = new Handle(Opcodes.H_INVOKESPECIAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
h = new Handle(Opcodes.H_INVOKESPECIAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc(), false);
break;
case MethodHandle.INVOKE_INTERFACE:
h = new Handle(Opcodes.H_INVOKEINTERFACE, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
h = new Handle(Opcodes.H_INVOKEINTERFACE, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc(), true);
break;
}
ele = h;

View File

@ -328,8 +328,12 @@ public abstract class TestUtils {
CfOptions cfOptions = new CfOptions();
cfOptions.strictNameCheck = false;
DexOptions dexOptions = new DexOptions();
if (fileNode != null && fileNode.dexVersion >= DexConstants.DEX_037) {
dexOptions.minSdkVersion = 26;
if (fileNode != null) {
if (fileNode.dexVersion >= DexConstants.DEX_039) {
dexOptions.minSdkVersion = 28;
} else if (fileNode.dexVersion >= DexConstants.DEX_037) {
dexOptions.minSdkVersion = 26;
}
}
DirectClassFile dcf = new DirectClassFile(data, rca.getClassName() + ".class", true);

Binary file not shown.

Binary file not shown.

View File

@ -391,7 +391,7 @@ public class CodeWriter extends DexCodeVisitor {
@Override
public void visitConstStmt(Op op, int ra, Object value) {
switch (op.format) {
case kFmt21c:// value is field,type,string
case kFmt21c:// value is field,type,string,method_handle,proto
case kFmt31c:// value is string,
value = cp.wrapEncodedItem(value);
ops.add(new CodeWriter.IndexedInsn(op, ra, 0, (BaseItem) value));

View File

@ -138,6 +138,8 @@ public class DexFileWriter extends DexFileVisitor {
SectionType.TYPE_FIELD_ID_ITEM, cp.fields.values());
SectionItem<MethodIdItem> methodIdSection = new SectionItem<>(
SectionType.TYPE_METHOD_ID_ITEM, cp.methods.values());
SectionItem<MethodHandleItem> methodHandlerSection = new SectionItem<>(
SectionType.TYPE_METHOD_HANDLE_ITEM, cp.methodHandlers.values());
SectionItem<ClassDefItem> classDefSection = new SectionItem<>(
SectionType.TYPE_CLASS_DEF_ITEM, cp.buildSortedClassDefItems());
SectionItem<TypeListItem> typeListSection = new SectionItem<>(
@ -200,6 +202,7 @@ public class DexFileWriter extends DexFileVisitor {
items.add(protoIdSection);
items.add(fieldIdSection);
items.add(methodIdSection);
items.add(methodHandlerSection);
items.add(classDefSection);
items.addAll(dataSectionItems);

View File

@ -19,6 +19,8 @@ package com.googlecode.d2j.dex.writer.item;
import com.googlecode.d2j.DexType;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.dex.writer.DexWriteException;
import java.util.*;
@ -40,6 +42,7 @@ public class ConstPool {
public Map<TypeListItem, TypeListItem> typeLists = new TreeMap<>();
public Map<String, TypeIdItem> types = new TreeMap<>();
public Map<TypeIdItem, ClassDefItem> classDefs = new HashMap<>();
public Map<MethodHandleItem, MethodHandleItem> methodHandlers = new TreeMap<>();
public Object wrapEncodedItem(Object value) {
if (value instanceof DexType) {
@ -50,10 +53,34 @@ public class ConstPool {
value = uniqString((String) value);
} else if (value instanceof Method) {
value = uniqMethod((Method) value);
} else if (value instanceof MethodHandle) {
value = uniqMethodHandle((MethodHandle) value);
} else if (value instanceof Proto) {
value = uniqProto((Proto) value);
}
return value;
}
private MethodHandleItem uniqMethodHandle(MethodHandle value) {
MethodHandleItem mh = new MethodHandleItem();
mh.type = value.getType();
Field field = value.getField();
Method method = value.getMethod();
if (field != null) {
mh.field = uniqField(field);
} else if (method != null) {
mh.method = uniqMethod(method);
}
MethodHandleItem result = methodHandlers.get(mh);
if (result == null) {
methodHandlers.put(mh, mh);
result = mh;
}
return result;
}
public void clean() {
encodedArrayItems.clear();
annotationSetRefListItems.clear();
@ -222,9 +249,12 @@ public class ConstPool {
return key;
}
private ProtoIdItem uniqProto(Method method) {
private ProtoIdItem uniqProto(Proto method) {
return uniqProto(method.getParameterTypes(), method.getReturnType());
}
private ProtoIdItem uniqProto(Method method) {
return uniqProto(method.getProto());
}
public ProtoIdItem uniqProto(String[] types, String retDesc) {
TypeIdItem ret = uniqType(retDesc);

View File

@ -85,6 +85,9 @@ public class FieldIdItem extends BaseItem implements Comparable<FieldIdItem> {
@Override
public int compareTo(FieldIdItem o) {
if (o == null) {
return 1;
}
int x = clazz.compareTo(o.clazz);
if (x != 0) {
return x;

View File

@ -0,0 +1,55 @@
package com.googlecode.d2j.dex.writer.item;
import com.googlecode.d2j.dex.writer.io.DataOut;
import java.util.Objects;
public class MethodHandleItem extends BaseItem implements Comparable<MethodHandleItem> {
public int type;
public FieldIdItem field;
public MethodIdItem method;
@Override
public void write(DataOut out) {
out.ushort("method_handle_type", type);
out.ushort("unused", 0);
out.ushort("field_or_method_id", field != null ? field.index : method.index);
out.ushort("unused", 0);
}
@Override
public int place(int offset) {
return offset + 8;
}
@Override
public int compareTo(MethodHandleItem o) {
if (o == null) {
return 1;
}
int x = Integer.compare(type, o.type);
if (x != 0) {
return x;
}
if (field != null) {
return field.compareTo(o.field);
} else if (method != null) {
return method.compareTo(o.method);
} else {
return -1;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodHandleItem that = (MethodHandleItem) o;
return type == that.type && Objects.equals(field, that.field) && Objects.equals(method, that.method);
}
@Override
public int hashCode() {
return Objects.hash(type, field, method);
}
}

View File

@ -81,6 +81,9 @@ public class MethodIdItem extends BaseItem implements Comparable<MethodIdItem> {
@Override
public int compareTo(MethodIdItem o) {
if (o == null) {
return 1;
}
int x = clazz.compareTo(o.clazz);
if (x != 0) {
return x;

View File

@ -110,6 +110,8 @@ public class SectionItem<T extends BaseItem> extends BaseItem {
TYPE_FIELD_ID_ITEM(0x0004, 4, 0), //
TYPE_METHOD_ID_ITEM(0x0005, 1, 0), //
TYPE_CLASS_DEF_ITEM(0x0006, 4, 0), //
TYPE_CALL_SITE_ID_ITEM(0x0007, 4, 0), //
TYPE_METHOD_HANDLE_ITEM(0x0008, 4, 0), //
TYPE_MAP_LIST(0x1000, 4, 0), //
TYPE_TYPE_LIST(0x1001, 4, 0), //
TYPE_ANNOTATION_SET_REF_LIST(0x1002, 4, 0), //