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 9213b2c2f..fdfb29062 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 2f94be4de..b5ef7b65f 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 Object visitFconst(SmaliParser.FconstContext ctx) { 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 Object visitFepiogue(SmaliParser.FepiogueContext ctx) { 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 682905943..78db7adb3 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 @@ static String escapeValue(Object obj) { 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 89e8ce77e..e1d4fae3e 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 @@ 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 static int reg2ParamIdx(Method m, int reg, int locals, boolean isStatic) 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/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 2db7ee4ef..5c16e4d54 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 static Constant nShort(short i) { 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/node/analysis/DvmFrame.java b/dex-reader-api/src/main/java/com/googlecode/d2j/node/analysis/DvmFrame.java index 3312194cb..56971b963 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 void execute(DexStmtNode insn, DvmInterpreter interpreter) { 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 void execute(DexStmtNode insn, DvmInterpreter interpreter) { 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 a50779528..07253e1b9 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 @@ 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 47fce9f75..1c1b7ea55 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 3e1118e6d..64941574d 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 void visitTypeStmt(Op op, int a, int b, String type) { } /** - *
-     * 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 44c60d695..be092cee1 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 @@ -1589,6 +1589,12 @@ private void acceptInsn(byte[] insns, DexCodeVisitor dcv, BitSet nextInsn, BitSe 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/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java b/dex-translator/src/main/java/com/googlecode/d2j/converter/Dex2IRConverter.java index 3fb02b031..d70c8200a 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.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 DvmValue newOperation(DexStmtNode insn) { 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 9bf8f2496..68c7c6a54 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 @@ 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 @@ private void accept(Value value, MethodVisitor asm) { 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 0066fe799..5e802bebc 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 static Object convertConstantValue(Object ele) { 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 03f114369..30c4f1fb6 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 ClassVisitor create(String classInternalName) { 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 000000000..7902f0199 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 000000000..31e9ecf98 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 11b14354d..941c9ea04 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 void visitFillArrayDataStmt(Op op, int ra, Object value) { @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 7bfc296e0..f8aa6517b 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 @@ void buildMapListItem() { 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 @@ void buildMapListItem() { 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 413313445..8ee9c2f95 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 @@ 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 Object wrapEncodedItem(Object value) { 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 MethodIdItem uniqMethod(MethodIdItem key) { 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 b41f48aa0..f70bb2e20 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 int place(int offset) { @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 000000000..d3ae54dd2 --- /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 5b282bb4c..c9db47de8 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 int place(int offset) { @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 7148d08d8..38d3a88c6 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 enum SectionType { 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), //