mirror of
https://github.com/pxb1988/dex2jar.git
synced 2024-11-23 13:19:46 +00:00
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:
commit
caf241d635
@ -21,6 +21,7 @@ subprojects {
|
||||
artifacts.archives packageSources
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
// == support provided scope
|
||||
|
@ -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 {
|
||||
|
@ -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'
|
||||
|
@ -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();
|
||||
|
@ -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 "?";
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
};
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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.
BIN
dex-translator/libs/dx-30.0.2.jar
Normal file
BIN
dex-translator/libs/dx-30.0.2.jar
Normal file
Binary file not shown.
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
BIN
dex-translator/src/test/resources/dexes/dex039.dex
Normal file
BIN
dex-translator/src/test/resources/dexes/dex039.dex
Normal file
Binary file not shown.
BIN
dex-translator/src/test/resources/dexes/dex040.dex
Normal file
BIN
dex-translator/src/test/resources/dexes/dex040.dex
Normal file
Binary file not shown.
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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), //
|
||||
|
Loading…
Reference in New Issue
Block a user