diff --git a/build.gradle b/build.gradle index dff58c85..8165f9a0 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ subprojects { artifacts.archives packageSources repositories { mavenCentral() + google() } // == support provided scope diff --git a/d2j-smali/build.gradle b/d2j-smali/build.gradle index 6398e61b..5cfacdea 100644 --- a/d2j-smali/build.gradle +++ b/d2j-smali/build.gradle @@ -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 { diff --git a/d2j-smali/src/main/antlr4/com/googlecode/d2j/smali/antlr4/Smali.g4 b/d2j-smali/src/main/antlr4/com/googlecode/d2j/smali/antlr4/Smali.g4 index 9213b2c2..fdfb2906 100644 --- a/d2j-smali/src/main/antlr4/com/googlecode/d2j/smali/antlr4/Smali.g4 +++ b/d2j-smali/src/main/antlr4/com/googlecode/d2j/smali/antlr4/Smali.g4 @@ -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' diff --git a/d2j-smali/src/main/java/com/googlecode/d2j/smali/AntlrSmaliUtil.java b/d2j-smali/src/main/java/com/googlecode/d2j/smali/AntlrSmaliUtil.java index 2f94be4d..b5ef7b65 100644 --- a/d2j-smali/src/main/java/com/googlecode/d2j/smali/AntlrSmaliUtil.java +++ b/d2j-smali/src/main/java/com/googlecode/d2j/smali/AntlrSmaliUtil.java @@ -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 instructionContexts = ctx.sInstruction(); diff --git a/d2j-smali/src/main/java/com/googlecode/d2j/smali/BaksmaliDumper.java b/d2j-smali/src/main/java/com/googlecode/d2j/smali/BaksmaliDumper.java index 68290594..78db7adb 100644 --- a/d2j-smali/src/main/java/com/googlecode/d2j/smali/BaksmaliDumper.java +++ b/d2j-smali/src/main/java/com/googlecode/d2j/smali/BaksmaliDumper.java @@ -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 "?"; diff --git a/d2j-smali/src/main/java/com/googlecode/d2j/smali/Utils.java b/d2j-smali/src/main/java/com/googlecode/d2j/smali/Utils.java index 89e8ce77..e1d4fae3 100644 --- a/d2j-smali/src/main/java/com/googlecode/d2j/smali/Utils.java +++ b/d2j-smali/src/main/java/com/googlecode/d2j/smali/Utils.java @@ -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 { diff --git a/d2j-smali/src/test/java/a/SmaliTest.java b/d2j-smali/src/test/java/a/SmaliTest.java index b5cbd826..a01a5e58 100644 --- a/d2j-smali/src/test/java/a/SmaliTest.java +++ b/d2j-smali/src/test/java/a/SmaliTest.java @@ -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(); } diff --git a/dex-ir/src/main/java/com/googlecode/dex2jar/ir/expr/Exprs.java b/dex-ir/src/main/java/com/googlecode/dex2jar/ir/expr/Exprs.java index 2db7ee4e..5c16e4d5 100644 --- a/dex-ir/src/main/java/com/googlecode/dex2jar/ir/expr/Exprs.java +++ b/dex-ir/src/main/java/com/googlecode/dex2jar/ir/expr/Exprs.java @@ -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); } diff --git a/dex-reader-api/src/main/java/com/googlecode/d2j/DexConstants.java b/dex-reader-api/src/main/java/com/googlecode/d2j/DexConstants.java index deaf5972..2740833e 100644 --- a/dex-reader-api/src/main/java/com/googlecode/d2j/DexConstants.java +++ b/dex-reader-api/src/main/java/com/googlecode/d2j/DexConstants.java @@ -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; } diff --git a/dex-reader-api/src/main/java/com/googlecode/d2j/node/analysis/DvmFrame.java b/dex-reader-api/src/main/java/com/googlecode/d2j/node/analysis/DvmFrame.java index 3312194c..56971b96 100644 --- a/dex-reader-api/src/main/java/com/googlecode/d2j/node/analysis/DvmFrame.java +++ b/dex-reader-api/src/main/java/com/googlecode/d2j/node/analysis/DvmFrame.java @@ -46,6 +46,8 @@ public class DvmFrame { 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 { setTmp(null); break; default: - throw new RuntimeException(); + throw new RuntimeException("not support " + insn.op); } } diff --git a/dex-reader-api/src/main/java/com/googlecode/d2j/reader/InstructionIndexType.java b/dex-reader-api/src/main/java/com/googlecode/d2j/reader/InstructionIndexType.java index a5077952..07253e1b 100644 --- a/dex-reader-api/src/main/java/com/googlecode/d2j/reader/InstructionIndexType.java +++ b/dex-reader-api/src/main/java/com/googlecode/d2j/reader/InstructionIndexType.java @@ -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 }; \ No newline at end of file diff --git a/dex-reader-api/src/main/java/com/googlecode/d2j/reader/Op.java b/dex-reader-api/src/main/java/com/googlecode/d2j/reader/Op.java index 47fce9f7..1c1b7ea5 100644 --- a/dex-reader-api/src/main/java/com/googlecode/d2j/reader/Op.java +++ b/dex-reader-api/src/main/java/com/googlecode/d2j/reader/Op.java @@ -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; diff --git a/dex-reader-api/src/main/java/com/googlecode/d2j/visitors/DexCodeVisitor.java b/dex-reader-api/src/main/java/com/googlecode/d2j/visitors/DexCodeVisitor.java index 3e1118e6..64941574 100644 --- a/dex-reader-api/src/main/java/com/googlecode/d2j/visitors/DexCodeVisitor.java +++ b/dex-reader-api/src/main/java/com/googlecode/d2j/visitors/DexCodeVisitor.java @@ -104,14 +104,23 @@ public class DexCodeVisitor { } /** - *
-     * CONST * CONST_WIDE * CONST_STRING * CONST_CLASS *
-     * 
- * - * @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) { diff --git a/dex-reader/src/main/java/com/googlecode/d2j/reader/DexFileReader.java b/dex-reader/src/main/java/com/googlecode/d2j/reader/DexFileReader.java index b9d3a4a3..be092cee 100755 --- a/dex-reader/src/main/java/com/googlecode/d2j/reader/DexFileReader.java +++ b/dex-reader/src/main/java/com/googlecode/d2j/reader/DexFileReader.java @@ -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; } diff --git a/dex-translator/libs/dx-27.0.3.jar b/dex-translator/libs/dx-27.0.3.jar deleted file mode 100644 index 023b8da3..00000000 Binary files a/dex-translator/libs/dx-27.0.3.jar and /dev/null differ diff --git a/dex-translator/libs/dx-30.0.2.jar b/dex-translator/libs/dx-30.0.2.jar new file mode 100644 index 00000000..ef90a14d Binary files /dev/null and b/dex-translator/libs/dx-30.0.2.jar differ diff --git a/dex-translator/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java b/dex-translator/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java index 3fb02b03..d70c8200 100644 --- a/dex-translator/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java @@ -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: diff --git a/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java b/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java index 9bf8f249..68c7c6a5 100755 --- a/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/converter/IR2JConverter.java @@ -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); } diff --git a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java index 0066fe79..5e802beb 100644 --- a/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java +++ b/dex-translator/src/main/java/com/googlecode/d2j/dex/Dex2Asm.java @@ -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; diff --git a/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java b/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java index 03f11436..30c4f1fb 100644 --- a/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java +++ b/dex-translator/src/test/java/com/googlecode/dex2jar/test/TestUtils.java @@ -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); diff --git a/dex-translator/src/test/resources/dexes/dex039.dex b/dex-translator/src/test/resources/dexes/dex039.dex new file mode 100644 index 00000000..7902f019 Binary files /dev/null and b/dex-translator/src/test/resources/dexes/dex039.dex differ diff --git a/dex-translator/src/test/resources/dexes/dex040.dex b/dex-translator/src/test/resources/dexes/dex040.dex new file mode 100644 index 00000000..31e9ecf9 Binary files /dev/null and b/dex-translator/src/test/resources/dexes/dex040.dex differ diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/CodeWriter.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/CodeWriter.java index 11b14354..941c9ea0 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/CodeWriter.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/CodeWriter.java @@ -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)); diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/DexFileWriter.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/DexFileWriter.java index 7bfc296e..f8aa6517 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/DexFileWriter.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/DexFileWriter.java @@ -138,6 +138,8 @@ public class DexFileWriter extends DexFileVisitor { SectionType.TYPE_FIELD_ID_ITEM, cp.fields.values()); SectionItem methodIdSection = new SectionItem<>( SectionType.TYPE_METHOD_ID_ITEM, cp.methods.values()); + SectionItem methodHandlerSection = new SectionItem<>( + SectionType.TYPE_METHOD_HANDLE_ITEM, cp.methodHandlers.values()); SectionItem classDefSection = new SectionItem<>( SectionType.TYPE_CLASS_DEF_ITEM, cp.buildSortedClassDefItems()); SectionItem 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); diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/ConstPool.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/ConstPool.java index 41331344..8ee9c2f9 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/ConstPool.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/ConstPool.java @@ -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 typeLists = new TreeMap<>(); public Map types = new TreeMap<>(); public Map classDefs = new HashMap<>(); + public Map 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); diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/FieldIdItem.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/FieldIdItem.java index b41f48aa..f70bb2e2 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/FieldIdItem.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/FieldIdItem.java @@ -85,6 +85,9 @@ public class FieldIdItem extends BaseItem implements Comparable { @Override public int compareTo(FieldIdItem o) { + if (o == null) { + return 1; + } int x = clazz.compareTo(o.clazz); if (x != 0) { return x; diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodHandleItem.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodHandleItem.java new file mode 100644 index 00000000..d3ae54dd --- /dev/null +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodHandleItem.java @@ -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 { + 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); + } +} diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodIdItem.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodIdItem.java index 5b282bb4..c9db47de 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodIdItem.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/MethodIdItem.java @@ -81,6 +81,9 @@ public class MethodIdItem extends BaseItem implements Comparable { @Override public int compareTo(MethodIdItem o) { + if (o == null) { + return 1; + } int x = clazz.compareTo(o.clazz); if (x != 0) { return x; diff --git a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/SectionItem.java b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/SectionItem.java index 7148d08d..38d3a88c 100644 --- a/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/SectionItem.java +++ b/dex-writer/src/main/java/com/googlecode/d2j/dex/writer/item/SectionItem.java @@ -110,6 +110,8 @@ public class SectionItem 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), //