mirror of
https://github.com/pxb1988/dex2jar.git
synced 2024-11-23 05:10:11 +00:00
'dex 038' support
This commit is contained in:
parent
dd9d7221e4
commit
6b08884da9
6
dex-ir/build.gradle
Normal file
6
dex-ir/build.gradle
Normal file
@ -0,0 +1,6 @@
|
||||
description = 'Dex Translator'
|
||||
|
||||
dependencies {
|
||||
compile project(':dex-reader-api')
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
|
||||
|
||||
public abstract class AbstractInvokeExpr extends EnExpr {
|
||||
@Override
|
||||
protected void releaseMemory() {
|
||||
super.releaseMemory();
|
||||
}
|
||||
|
||||
public abstract Proto getProto();
|
||||
|
||||
public AbstractInvokeExpr(VT type, Value[] args) {
|
||||
super(type, args);
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.MethodHandle;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.dex2jar.ir.expr.Value.VT;
|
||||
|
||||
public final class Exprs {
|
||||
@ -188,6 +191,14 @@ public final class Exprs {
|
||||
return new InvokeExpr(VT.INVOKE_VIRTUAL, regs, owner, name, argmentTypes, returnType);
|
||||
}
|
||||
|
||||
public static InvokeCustomExpr nInvokeCustom(Value[] regs, String name, Proto proto, MethodHandle handle, Object[] bsmArgs) {
|
||||
return new InvokeCustomExpr(VT.INVOKE_CUSTOM, regs , name, proto, handle, bsmArgs);
|
||||
}
|
||||
|
||||
public static InvokePolymorphicExpr nInvokePolymorphic(Value[] regs, Proto proto, Method method) {
|
||||
return new InvokePolymorphicExpr(VT.INVOKE_POLYMORPHIC, regs , proto, method);
|
||||
}
|
||||
|
||||
public static BinopExpr nLCmp(Value a, Value b) {
|
||||
return new BinopExpr(VT.LCMP, a, b, "J");
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
import com.googlecode.d2j.MethodHandle;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
|
||||
|
||||
public class InvokeCustomExpr extends AbstractInvokeExpr {
|
||||
public String name;
|
||||
public Proto proto;
|
||||
public MethodHandle handle;
|
||||
public Object[] bsmArgs;
|
||||
|
||||
@Override
|
||||
protected void releaseMemory() {
|
||||
name = null;
|
||||
proto = null;
|
||||
handle = null;
|
||||
bsmArgs = null;
|
||||
super.releaseMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return proto;
|
||||
}
|
||||
|
||||
public InvokeCustomExpr(VT type, Value[] args, String methodName, Proto proto, MethodHandle handle, Object[] bsmArgs) {
|
||||
super(type, args);
|
||||
this.proto = proto;
|
||||
this.name = methodName;
|
||||
this.handle = handle;
|
||||
this.bsmArgs = bsmArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
return new InvokeCustomExpr(vt, cloneOps(), name, proto, handle, bsmArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone(LabelAndLocalMapper mapper) {
|
||||
return new InvokeCustomExpr(vt, cloneOps(mapper), name, proto, handle, bsmArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString0() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("InvokeCustomExpr(....)");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
|
||||
import com.googlecode.dex2jar.ir.Util;
|
||||
import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
|
||||
@ -22,60 +24,50 @@ import com.googlecode.dex2jar.ir.expr.Value.EnExpr;
|
||||
/**
|
||||
* Represent a method invocation expression. To represent a {@link VT#INVOKE_INTERFACE},{@link VT#INVOKE_SPECIAL} or
|
||||
* {@link VT#INVOKE_VIRTUAL} the first element of ops is the owner object,To represent a {@link VT#INVOKE_NEW} or
|
||||
* {@link VT#INVOKE_STATIC} all ops are arguments. The return type of {@link VT#INVOKE_NEW} is {@link #owner} instead of
|
||||
* {@link #ret}
|
||||
*
|
||||
* {@link VT#INVOKE_STATIC} all ops are arguments. The return type of {@link VT#INVOKE_NEW} is owner instead of ret
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
* @version $Rev: 9fd8005bbaa4 $
|
||||
* @see VT#INVOKE_INTERFACE
|
||||
* @see VT#INVOKE_NEW
|
||||
* @see VT#INVOKE_SPECIAL
|
||||
* @see VT#INVOKE_STATIC
|
||||
* @see VT#INVOKE_VIRTUAL
|
||||
*
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
* @version $Rev: 9fd8005bbaa4 $
|
||||
*/
|
||||
public class InvokeExpr extends EnExpr {
|
||||
public class InvokeExpr extends AbstractInvokeExpr {
|
||||
|
||||
/**
|
||||
* argument type desc
|
||||
*/
|
||||
public String[] args;
|
||||
public String name;
|
||||
/**
|
||||
* owner type desc
|
||||
*/
|
||||
public String owner;
|
||||
/**
|
||||
* owner type desc
|
||||
*/
|
||||
public String ret;
|
||||
public Method method;
|
||||
|
||||
@Override
|
||||
protected void releaseMemory() {
|
||||
args = null;
|
||||
ret = null;
|
||||
owner = null;
|
||||
name = null;
|
||||
method = null;
|
||||
super.releaseMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return method.getProto();
|
||||
}
|
||||
|
||||
public InvokeExpr(VT type, Value[] args, String ownerType, String methodName, String[] argmentTypes,
|
||||
String returnType) {
|
||||
String returnType) {
|
||||
super(type, args);
|
||||
this.ret = returnType;
|
||||
this.name = methodName;
|
||||
this.owner = ownerType;
|
||||
this.args = argmentTypes;
|
||||
this.method = new Method(ownerType, methodName, argmentTypes, returnType);
|
||||
}
|
||||
|
||||
public InvokeExpr(VT type, Value[] args, Method method) {
|
||||
super(type, args);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
return new InvokeExpr(vt, cloneOps(), owner, name, args, ret);
|
||||
return new InvokeExpr(vt, cloneOps(), method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone(LabelAndLocalMapper mapper) {
|
||||
return new InvokeExpr(vt, cloneOps(mapper), owner, name, args, ret);
|
||||
return new InvokeExpr(vt, cloneOps(mapper), method);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,10 +75,10 @@ public class InvokeExpr extends EnExpr {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (super.vt == VT.INVOKE_NEW) {
|
||||
sb.append("new ").append(Util.toShortClassName(owner)).append('(');
|
||||
sb.append("new ").append(Util.toShortClassName(method.getOwner())).append('(');
|
||||
} else {
|
||||
sb.append(super.vt == VT.INVOKE_STATIC ? Util.toShortClassName(owner) : ops[0]).append('.')
|
||||
.append(this.name).append('(');
|
||||
sb.append(super.vt == VT.INVOKE_STATIC ? Util.toShortClassName(method.getOwner()) : ops[0]).append('.')
|
||||
.append(this.method.getName()).append('(');
|
||||
}
|
||||
boolean first = true;
|
||||
for (int i = (vt == VT.INVOKE_STATIC || vt == VT.INVOKE_NEW) ? 0 : 1; i < ops.length; i++) {
|
||||
@ -100,4 +92,21 @@ public class InvokeExpr extends EnExpr {
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return method.getOwner();
|
||||
}
|
||||
|
||||
public String getRet() {
|
||||
return method.getReturnType();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
return method.getParameterTypes();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.dex2jar.ir.expr;
|
||||
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.dex2jar.ir.LabelAndLocalMapper;
|
||||
|
||||
public class InvokePolymorphicExpr extends AbstractInvokeExpr {
|
||||
public Proto proto;
|
||||
public Method method;
|
||||
|
||||
@Override
|
||||
protected void releaseMemory() {
|
||||
method = null;
|
||||
proto = null;
|
||||
super.releaseMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return proto;
|
||||
}
|
||||
|
||||
public InvokePolymorphicExpr(VT type, Value[] args, Proto proto, Method method) {
|
||||
super(type, args);
|
||||
this.proto = proto;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
return new InvokePolymorphicExpr(vt, cloneOps(), proto, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone(LabelAndLocalMapper mapper) {
|
||||
return new InvokePolymorphicExpr(vt, cloneOps(mapper), proto, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString0() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("InvokePolymorphicExpr(...)");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -179,7 +179,7 @@ public abstract class Value implements Cloneable {
|
||||
MAY_THROW), DCMPL(MAY_THROW), IDIV("/", CAN_THROW), LDIV("/", CAN_THROW), FDIV("/", MAY_THROW), DDIV("/", MAY_THROW), EQ("==", MAY_THROW), EXCEPTION_REF(0), FCMPG(
|
||||
MAY_THROW), FCMPL(MAY_THROW), FIELD(CAN_THROW), FILLED_ARRAY(CAN_THROW), GE(">=", MAY_THROW), GT(">",
|
||||
MAY_THROW), INSTANCE_OF(CAN_THROW), INVOKE_INTERFACE(CAN_THROW), //
|
||||
INVOKE_NEW(CAN_THROW), INVOKE_SPECIAL(CAN_THROW), INVOKE_STATIC(CAN_THROW), INVOKE_VIRTUAL(CAN_THROW), LCMP(
|
||||
INVOKE_NEW(CAN_THROW), INVOKE_SPECIAL(CAN_THROW), INVOKE_STATIC(CAN_THROW), INVOKE_VIRTUAL(CAN_THROW), INVOKE_CUSTOM(CAN_THROW), INVOKE_POLYMORPHIC(CAN_THROW), LCMP(
|
||||
MAY_THROW), //
|
||||
LE("<=", MAY_THROW), LENGTH(CAN_THROW), LOCAL(0), LT("<", MAY_THROW), MUL("*", MAY_THROW), NE("!=", MAY_THROW), NEG(
|
||||
MAY_THROW), //
|
||||
|
@ -139,7 +139,7 @@ public class AggTransformer extends StatedTransformer {
|
||||
boolean shouldExclude = false;
|
||||
if (op.vt == Value.VT.INVOKE_STATIC) {
|
||||
InvokeExpr ie = (InvokeExpr) op;
|
||||
if (ie.name.equals("valueOf") && ie.owner.startsWith("Ljava/lang/") && ie.args.length == 1 && ie.args[0].length() == 1) {
|
||||
if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/") && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) {
|
||||
shouldExclude = true;
|
||||
}
|
||||
}
|
||||
@ -318,7 +318,7 @@ public class AggTransformer extends StatedTransformer {
|
||||
case En:
|
||||
if (op.vt == Value.VT.INVOKE_STATIC) {
|
||||
InvokeExpr ie = (InvokeExpr) op;
|
||||
if (ie.name.equals("valueOf") && ie.owner.startsWith("Ljava/lang/") && ie.args.length == 1 && ie.args[0].length() == 1) {
|
||||
if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/") && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) {
|
||||
for (Value v : op.getOps()) {
|
||||
if (!isLocationInsensitive(v)) {
|
||||
return false;
|
||||
|
@ -41,10 +41,10 @@ public class MultiArrayTransformer extends StatedTransformer {
|
||||
Value from = te.getOp();
|
||||
if (from.vt == Value.VT.INVOKE_STATIC) {
|
||||
InvokeExpr invokeExpr = (InvokeExpr) from;
|
||||
if (invokeExpr.name.equals("newInstance")
|
||||
&& invokeExpr.owner.equals("Ljava/lang/reflect/Array;")
|
||||
&& invokeExpr.args.length == 2
|
||||
&& invokeExpr.args[0].equals("Ljava/lang/Class;")) {
|
||||
if (invokeExpr.getName().equals("newInstance")
|
||||
&& invokeExpr.getOwner().equals("Ljava/lang/reflect/Array;")
|
||||
&& invokeExpr.getArgs().length == 2
|
||||
&& invokeExpr.getArgs()[0].equals("Ljava/lang/Class;")) {
|
||||
Value arg0 = invokeExpr.getOps()[0];
|
||||
String elementType = null;
|
||||
if (arg0.vt == Value.VT.CONSTANT) {
|
||||
@ -88,7 +88,7 @@ public class MultiArrayTransformer extends StatedTransformer {
|
||||
}
|
||||
if (elementType != null) {
|
||||
Value dt = invokeExpr.getOps()[1];
|
||||
if (invokeExpr.args[1].equals("I")) {
|
||||
if (invokeExpr.getArgs()[1].equals("I")) {
|
||||
if (te.type.equals("[" + elementType)) {
|
||||
int d = 0;
|
||||
while (elementType.charAt(d) == '[') {
|
||||
|
@ -98,14 +98,14 @@ public class NewTransformer implements Transformer {
|
||||
InvokeExpr ie = findInvokeExpr(p, null);
|
||||
|
||||
if (ie != null) {
|
||||
if ("<init>".equals(ie.name) && "V".equals(ie.ret)) {
|
||||
if ("<init>".equals(ie.getName()) && "V".equals(ie.getRet())) {
|
||||
Value[] orgOps = ie.getOps();
|
||||
if (orgOps[0].vt == NEW) {
|
||||
NewExpr newExpr = (NewExpr) ie.getOps()[0];
|
||||
if (newExpr != null) {
|
||||
Value[] nOps = new Value[orgOps.length - 1];
|
||||
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
|
||||
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
|
||||
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.getArgs(), ie.getOwner());
|
||||
method.stmts.insertBefore(p, Stmts.nVoidInvoke(invokeNew));
|
||||
it.remove();
|
||||
}
|
||||
@ -158,7 +158,7 @@ public class NewTransformer implements Transformer {
|
||||
Value[] orgOps = ie.getOps();
|
||||
Value[] nOps = new Value[orgOps.length - 1];
|
||||
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
|
||||
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
|
||||
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.getArgs(), ie.getOwner());
|
||||
method.stmts.replace(obj.invokeStmt, Stmts.nAssign(obj.local, invokeNew));
|
||||
}
|
||||
}
|
||||
@ -211,7 +211,7 @@ public class NewTransformer implements Transformer {
|
||||
if (op.vt == INVOKE_SPECIAL) {
|
||||
if (op.getOps().length >= 1) {
|
||||
InvokeExpr ie = (InvokeExpr) op;
|
||||
if ("<init>".equals(ie.name)) {
|
||||
if ("<init>".equals(ie.getName())) {
|
||||
Value thiz = op.getOps()[0];
|
||||
if (thiz.vt == LOCAL) {
|
||||
Local local = (Local) thiz;
|
||||
|
@ -917,20 +917,29 @@ public class TypeTransformer implements Transformer {
|
||||
case INVOKE_SPECIAL:
|
||||
case INVOKE_STATIC:
|
||||
case INVOKE_VIRTUAL:
|
||||
InvokeExpr ie = (InvokeExpr) enExpr;
|
||||
String type = ie.vt == VT.INVOKE_NEW ? ie.owner : ie.ret;
|
||||
case INVOKE_POLYMORPHIC:
|
||||
case INVOKE_CUSTOM: {
|
||||
AbstractInvokeExpr ice = (AbstractInvokeExpr) enExpr;
|
||||
String type = ice.getProto().getReturnType();
|
||||
provideAs(enExpr, type);
|
||||
useAs(enExpr, type); // no one else will use it
|
||||
|
||||
int start = 0;
|
||||
if (ie.vt != VT.INVOKE_STATIC && ie.vt != VT.INVOKE_NEW) {
|
||||
start = 1;
|
||||
useAs(vbs[0], ie.owner);
|
||||
String argTypes[] = ice.getProto().getParameterTypes();
|
||||
if (argTypes.length == vbs.length) {
|
||||
for (int i = 0; i < vbs.length; i++) {
|
||||
useAs(vbs[i], argTypes[i]);
|
||||
}
|
||||
} else if (argTypes.length + 1 == vbs.length) {
|
||||
useAs(vbs[0], "L");
|
||||
for (int i = 1; i < vbs.length; i++) {
|
||||
useAs(vbs[i], argTypes[i - 1]);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
for (int i = 0; start < vbs.length; start++, i++) {
|
||||
useAs(vbs[start], ie.args[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case FILLED_ARRAY:
|
||||
FilledArrayExpr fae = (FilledArrayExpr) enExpr;
|
||||
for (Value vb : vbs) {
|
||||
|
@ -24,10 +24,6 @@ import java.util.Arrays;
|
||||
* @version $Rev$
|
||||
*/
|
||||
public class Method {
|
||||
/**
|
||||
* descriptor of the method, this will build after {@link #getDesc()}.
|
||||
*/
|
||||
private String desc;
|
||||
/**
|
||||
* name of the method.
|
||||
*/
|
||||
@ -39,32 +35,24 @@ public class Method {
|
||||
/**
|
||||
* parameter types of the method, in TypeDescriptor format.
|
||||
*/
|
||||
private String[] parameterTypes;
|
||||
private Proto proto;
|
||||
|
||||
/**
|
||||
* return type of the method, in TypeDescriptor format.
|
||||
*/
|
||||
private String returnType;
|
||||
public Proto getProto() {
|
||||
return proto;
|
||||
}
|
||||
|
||||
public Method(String owner, String name, String[] parameterTypes, String returnType) {
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.returnType = returnType;
|
||||
this.proto = new Proto(parameterTypes, returnType);
|
||||
}
|
||||
public Method(String owner, String name, Proto proto) {
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.proto = proto;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
if (desc == null) {
|
||||
StringBuilder ps = new StringBuilder("(");
|
||||
if (parameterTypes != null) {
|
||||
for (String t : parameterTypes) {
|
||||
ps.append(t);
|
||||
}
|
||||
}
|
||||
ps.append(")").append(returnType);
|
||||
desc = ps.toString();
|
||||
}
|
||||
return desc;
|
||||
return proto.getDesc();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,68 +73,38 @@ public class Method {
|
||||
* @return the parameterTypes
|
||||
*/
|
||||
public String[] getParameterTypes() {
|
||||
return parameterTypes;
|
||||
return proto.getParameterTypes();
|
||||
}
|
||||
|
||||
public String getReturnType() {
|
||||
return returnType;
|
||||
return proto.getReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Method method = (Method) o;
|
||||
|
||||
if (name != null ? !name.equals(method.name) : method.name != null) return false;
|
||||
if (owner != null ? !owner.equals(method.owner) : method.owner != null) return false;
|
||||
return proto.equals(method.proto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
|
||||
result = prime * result + Arrays.hashCode(parameterTypes);
|
||||
result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (owner != null ? owner.hashCode() : 0);
|
||||
result = 31 * result + proto.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Method other = (Method) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
if (owner == null) {
|
||||
if (other.owner != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!owner.equals(other.owner)) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(parameterTypes, other.parameterTypes)) {
|
||||
return false;
|
||||
}
|
||||
if (returnType == null) {
|
||||
if (other.returnType != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!returnType.equals(other.returnType)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getOwner() + "." + this.getName() + this.getDesc();
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j;
|
||||
|
||||
public class MethodHandle {
|
||||
public static final int STATIC_PUT = 0x00;
|
||||
public static final int STATIC_GET = 0x01;
|
||||
public static final int INSTANCE_PUT = 0x02;
|
||||
public static final int INSTANCE_GET = 0x03;
|
||||
public static final int INVOKE_STATIC = 0x04;
|
||||
public static final int INVOKE_INSTANCE = 0x05;
|
||||
private int type;
|
||||
private Field field;
|
||||
private Method method;
|
||||
|
||||
public MethodHandle(int type, Field field) {
|
||||
this.type = type;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public MethodHandle(int type, Method method) {
|
||||
this.type = type;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public MethodHandle(int type, Field field, Method method) {
|
||||
this.type = type;
|
||||
this.field = field;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MethodHandle that = (MethodHandle) o;
|
||||
|
||||
if (type != that.type) return false;
|
||||
if (field != null ? !field.equals(that.field) : that.field != null) return false;
|
||||
return method != null ? method.equals(that.method) : that.method == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = type;
|
||||
result = 31 * result + (field != null ? field.hashCode() : 0);
|
||||
result = 31 * result + (method != null ? method.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
}
|
83
dex-reader-api/src/main/java/com/googlecode/d2j/Proto.java
Normal file
83
dex-reader-api/src/main/java/com/googlecode/d2j/Proto.java
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Proto {
|
||||
public Proto(String[] parameterTypes, String returnType) {
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* descriptor of the method, this will build after {@link #getDesc()}.
|
||||
*/
|
||||
private String desc;
|
||||
/**
|
||||
* parameter types of the method, in TypeDescriptor format.
|
||||
*/
|
||||
private String[] parameterTypes;
|
||||
|
||||
/**
|
||||
* return type of the method, in TypeDescriptor format.
|
||||
*/
|
||||
private String returnType;
|
||||
|
||||
/**
|
||||
* @return the parameterTypes
|
||||
*/
|
||||
public String[] getParameterTypes() {
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
public String getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
if (desc == null) {
|
||||
StringBuilder ps = new StringBuilder("(");
|
||||
if (parameterTypes != null) {
|
||||
for (String t : parameterTypes) {
|
||||
ps.append(t);
|
||||
}
|
||||
}
|
||||
ps.append(")").append(returnType);
|
||||
desc = ps.toString();
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Proto proto = (Proto) o;
|
||||
|
||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
||||
if (!Arrays.equals(parameterTypes, proto.parameterTypes)) return false;
|
||||
return returnType != null ? returnType.equals(proto.returnType) : proto.returnType == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(parameterTypes);
|
||||
result = 31 * result + (returnType != null ? returnType.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -19,9 +19,7 @@ package com.googlecode.d2j.node;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.googlecode.d2j.DexLabel;
|
||||
import com.googlecode.d2j.Field;
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.*;
|
||||
import com.googlecode.d2j.node.insn.*;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
import com.googlecode.d2j.visitors.DexCodeVisitor;
|
||||
@ -111,6 +109,16 @@ public class DexCodeNode extends DexCodeVisitor {
|
||||
add(new MethodStmtNode(op, args, method));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
|
||||
add(new MethodCustomStmtNode(op, args, name, proto, bsm, bsmArgs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
|
||||
add(new MethodPolymorphicStmtNode(op, args, bsm, proto));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPackedSwitchStmt(final Op op, final int aA, final int first_case, final DexLabel[] labels) {
|
||||
add(new PackedSwitchStmtNode(op, aA, first_case, labels));
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.googlecode.d2j.node.analysis;
|
||||
|
||||
import com.googlecode.d2j.MethodHandle;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.d2j.node.insn.*;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
|
||||
@ -259,18 +261,29 @@ public class DvmFrame<V> {
|
||||
case INVOKE_STATIC_RANGE:
|
||||
case INVOKE_STATIC:
|
||||
case INVOKE_INTERFACE_RANGE:
|
||||
case INVOKE_INTERFACE: {
|
||||
case INVOKE_INTERFACE:
|
||||
case INVOKE_CUSTOM:
|
||||
case INVOKE_CUSTOM_RANGE:
|
||||
case INVOKE_POLYMORPHIC:
|
||||
case INVOKE_POLYMORPHIC_RANGE: {
|
||||
int i = 0;
|
||||
MethodStmtNode methodStmtNode = (MethodStmtNode) insn;
|
||||
AbstractMethodStmtNode methodStmtNode = (AbstractMethodStmtNode) insn;
|
||||
List<V> v;
|
||||
Proto proto = methodStmtNode.getProto();
|
||||
boolean isStatic = false;
|
||||
if (insn.op == Op.INVOKE_STATIC || insn.op == Op.INVOKE_STATIC_RANGE) {
|
||||
v = new ArrayList<>(methodStmtNode.method.getParameterTypes().length);
|
||||
isStatic = true;
|
||||
} else if (insn.op == Op.INVOKE_CUSTOM || insn.op == Op.INVOKE_CUSTOM_RANGE) {
|
||||
isStatic = true;
|
||||
}
|
||||
if (isStatic) {
|
||||
v = new ArrayList<>(proto.getParameterTypes().length);
|
||||
} else {
|
||||
v = new ArrayList<>(methodStmtNode.method.getParameterTypes().length + 1);
|
||||
v = new ArrayList<>(proto.getParameterTypes().length + 1);
|
||||
v.add(getReg(methodStmtNode.args[i++]));
|
||||
}
|
||||
|
||||
for (String type : methodStmtNode.method.getParameterTypes()) {
|
||||
for (String type : proto.getParameterTypes()) {
|
||||
v.add(getReg(methodStmtNode.args[i]));
|
||||
char t = type.charAt(0);
|
||||
if (t == 'J' || t == 'D') {
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j.node.insn;
|
||||
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
|
||||
public abstract class AbstractMethodStmtNode extends DexStmtNode {
|
||||
public final int[] args;
|
||||
|
||||
public AbstractMethodStmtNode(Op op, int[] args) {
|
||||
super(op);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public abstract Proto getProto();
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j.node.insn;
|
||||
|
||||
import com.googlecode.d2j.MethodHandle;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
import com.googlecode.d2j.visitors.DexCodeVisitor;
|
||||
|
||||
public class MethodCustomStmtNode extends AbstractMethodStmtNode {
|
||||
public final String name;
|
||||
public final Proto proto;
|
||||
public final MethodHandle bsm;
|
||||
public final Object[] bsmArgs;
|
||||
|
||||
public MethodCustomStmtNode(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object[] bsmArgs) {
|
||||
super(op, args);
|
||||
this.proto = proto;
|
||||
this.name = name;
|
||||
this.bsm = bsm;
|
||||
this.bsmArgs = bsmArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DexCodeVisitor cv) {
|
||||
cv.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return proto;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j.node.insn;
|
||||
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
import com.googlecode.d2j.visitors.DexCodeVisitor;
|
||||
|
||||
public class MethodPolymorphicStmtNode extends AbstractMethodStmtNode {
|
||||
public final Method method;
|
||||
public final Proto proto;
|
||||
|
||||
public MethodPolymorphicStmtNode(Op op, int[] args, Method method, Proto proto) {
|
||||
super(op, args);
|
||||
this.method = method;
|
||||
this.proto = proto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DexCodeVisitor cv) {
|
||||
cv.visitMethodStmt(op, args, method, proto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return proto;
|
||||
}
|
||||
}
|
@ -1,16 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2017 Panxiaobo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.googlecode.d2j.node.insn;
|
||||
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.Proto;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
import com.googlecode.d2j.visitors.DexCodeVisitor;
|
||||
|
||||
public class MethodStmtNode extends DexStmtNode {
|
||||
public final int[] args;
|
||||
public class MethodStmtNode extends AbstractMethodStmtNode {
|
||||
public final Method method;
|
||||
|
||||
public MethodStmtNode(Op op, int[] args, Method method) {
|
||||
super(op);
|
||||
this.args = args;
|
||||
super(op, args);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@ -18,4 +32,9 @@ public class MethodStmtNode extends DexStmtNode {
|
||||
public void accept(DexCodeVisitor cv) {
|
||||
cv.visitMethodStmt(op, args, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proto getProto() {
|
||||
return method.getProto();
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ public enum InstructionFormat {
|
||||
// kFmt35ms(3), // [opt] invoke-virtual+super
|
||||
kFmt3rc(3), // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
|
||||
// kFmt3rms(3), // [opt] invoke-virtual+super/range
|
||||
kFmt45cc(4), // op {vC,vD,vE,vF,vG}, meth@BBBB, proto@HHHH
|
||||
kFmt4rcc(4), // op {vCCCC .. vNNNNN}, meth@BBBB, proto@HHHH
|
||||
kFmt51l(5), // op vAA, #+BBBBBBBBBBBBBBBB
|
||||
// kFmt35mi(3), // [opt] inline invoke
|
||||
// kFmt3rmi(3), // [opt] inline invoke/range
|
||||
|
@ -10,5 +10,7 @@ package com.googlecode.d2j.reader;
|
||||
kIndexFieldRef, // field reference index
|
||||
kIndexInlineMethod, // inline method index (for inline linked methods)
|
||||
kIndexVtableOffset, // vtable offset (for static linked methods)
|
||||
kIndexFieldOffset // field offset (for static linked fields)
|
||||
kIndexFieldOffset, // field offset (for static linked fields)
|
||||
kIndexMethodAndProtoRef, // 038,
|
||||
kIndexCallSiteRef, // 038,
|
||||
};
|
@ -117,6 +117,10 @@ public enum Op implements CFG {
|
||||
SPUT_SHORT(0x6d, "sput-short", kFmt21c, kIndexFieldRef, kInstrCanContinue | kInstrCanThrow, false), //
|
||||
INVOKE_VIRTUAL(0x6e, "invoke-virtual", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
|
||||
true), //
|
||||
|
||||
/**
|
||||
* Behavior changed in 037, interface method is allowed
|
||||
*/
|
||||
INVOKE_SUPER(0x6f, "invoke-super", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
|
||||
true), //
|
||||
INVOKE_DIRECT(0x70, "invoke-direct", kFmt35c, kIndexMethodRef, kInstrCanContinue | kInstrCanThrow | kInstrInvoke,
|
||||
@ -242,6 +246,14 @@ public enum Op implements CFG {
|
||||
SHL_INT_LIT8(0xe0, "shl-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
|
||||
SHR_INT_LIT8(0xe1, "shr-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
|
||||
USHR_INT_LIT8(0xe2, "ushr-int/lit8", kFmt22b, kIndexNone, kInstrCanContinue, true), //
|
||||
INVOKE_POLYMORPHIC(0xfa, "invoke-polymorphic", kFmt45cc, kIndexMethodAndProtoRef, kInstrCanContinue | kInstrCanThrow
|
||||
| kInstrInvoke, true), //
|
||||
INVOKE_POLYMORPHIC_RANGE(0xfb, "invoke-polymorphic/range", kFmt4rcc, kIndexMethodAndProtoRef, kInstrCanContinue | kInstrCanThrow
|
||||
| kInstrInvoke, true), //
|
||||
INVOKE_CUSTOM(0xfc, "invoke-custom", kFmt35c, kIndexCallSiteRef, kInstrCanContinue | kInstrCanThrow
|
||||
| kInstrInvoke, true), //
|
||||
INVOKE_CUSTOM_RANGE(0xfd, "invoke-custom/range", kFmt3rc, kIndexCallSiteRef, kInstrCanContinue | kInstrCanThrow
|
||||
| kInstrInvoke, true), //
|
||||
BAD_OP(-1, "bad-opcode", null, kIndexNone, 0, false), //
|
||||
;
|
||||
public int opcode;
|
||||
|
@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.googlecode.d2j.visitors;
|
||||
|
||||
import com.googlecode.d2j.DexLabel;
|
||||
import com.googlecode.d2j.Field;
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.*;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
|
||||
/**
|
||||
@ -226,6 +224,29 @@ public class DexCodeVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* OP_INVOKE_CUSTOM
|
||||
* </pre>
|
||||
*/
|
||||
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
|
||||
if (visitor != null) {
|
||||
visitor.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* OP_INVOKE_POLYMORPHIC
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
|
||||
if (visitor != null) {
|
||||
visitor.visitMethodStmt(op, args, bsm, proto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* OP_MOVE*
|
||||
|
@ -5,6 +5,12 @@ import com.googlecode.d2j.visitors.DexFileVisitor;
|
||||
import java.util.List;
|
||||
|
||||
public interface BaseDexFileReader {
|
||||
int DEX_035 = 0x00303335;
|
||||
int DEX_036 = 0x00303336;
|
||||
int DEX_037 = 0x00303337;
|
||||
int DEX_038 = 0x00303338;
|
||||
|
||||
int getDexVersion();
|
||||
|
||||
void accept(DexFileVisitor dv);
|
||||
|
||||
|
@ -84,27 +84,30 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
static final int DBG_FIRST_SPECIAL = 0x0a;
|
||||
static final int DBG_LINE_BASE = -4;
|
||||
static final int DBG_LINE_RANGE = 15;
|
||||
private static final int MAGIC_DEX = 0x0A786564 & 0x00FFFFFF;// hex for 'dex ', ignore the 0A
|
||||
private static final int MAGIC_ODEX = 0x0A796564 & 0x00FFFFFF;// hex for 'dey ', ignore the 0A
|
||||
private static final int MAGIC_035 = 0x00353330;
|
||||
private static final int MAGIC_036 = 0x00363330;
|
||||
|
||||
private static final int ENDIAN_CONSTANT = 0x12345678;
|
||||
private static final int VALUE_BYTE = 0;
|
||||
private static final int VALUE_SHORT = 2;
|
||||
private static final int VALUE_CHAR = 3;
|
||||
private static final int VALUE_INT = 4;
|
||||
private static final int VALUE_LONG = 6;
|
||||
private static final int VALUE_FLOAT = 16;
|
||||
private static final int VALUE_DOUBLE = 17;
|
||||
private static final int VALUE_STRING = 23;
|
||||
private static final int VALUE_TYPE = 24;
|
||||
private static final int VALUE_FIELD = 25;
|
||||
private static final int VALUE_METHOD = 26;
|
||||
private static final int VALUE_ENUM = 27;
|
||||
private static final int VALUE_ARRAY = 28;
|
||||
private static final int VALUE_ANNOTATION = 29;
|
||||
private static final int VALUE_NULL = 30;
|
||||
private static final int VALUE_BOOLEAN = 31;
|
||||
private static final int VALUE_BYTE = 0x00;
|
||||
private static final int VALUE_SHORT = 0x02;
|
||||
private static final int VALUE_CHAR = 0x03;
|
||||
private static final int VALUE_INT = 0x04;
|
||||
private static final int VALUE_LONG = 0x06;
|
||||
private static final int VALUE_FLOAT = 0x10;
|
||||
private static final int VALUE_DOUBLE = 0x11;
|
||||
private static final int VALUE_METHOD_TYPE = 0x15;
|
||||
private static final int VALUE_METHOD_HANDLE = 0x16;
|
||||
private static final int VALUE_STRING = 0x17;
|
||||
private static final int VALUE_TYPE = 0x18;
|
||||
private static final int VALUE_FIELD = 0x19;
|
||||
private static final int VALUE_METHOD = 0x1a;
|
||||
private static final int VALUE_ENUM = 0x1b;
|
||||
private static final int VALUE_ARRAY = 0x1c;
|
||||
private static final int VALUE_ANNOTATION = 0x1d;
|
||||
private static final int VALUE_NULL = 0x1e;
|
||||
private static final int VALUE_BOOLEAN = 0x1f;
|
||||
|
||||
private static final int TYPE_CALL_SITE_ID_ITEM = 0x0007;
|
||||
private static final int TYPE_METHOD_HANDLE_ITEM = 0x0008;
|
||||
|
||||
final ByteBuffer annotationSetRefListIn;
|
||||
final ByteBuffer annotationsDirectoryItemIn;
|
||||
final ByteBuffer annotationSetItemIn;
|
||||
@ -121,11 +124,17 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
final ByteBuffer typeListIn;
|
||||
final ByteBuffer stringDataIn;
|
||||
final ByteBuffer debugInfoIn;
|
||||
final ByteBuffer callSiteIdIn;
|
||||
final ByteBuffer methodHandleIdIn;
|
||||
final int string_ids_size;
|
||||
final int type_ids_size;
|
||||
final int proto_ids_size;
|
||||
final int field_ids_size;
|
||||
final int method_ids_size;
|
||||
final private int class_defs_size;
|
||||
final int call_site_ids_size;
|
||||
final int method_handle_ids_size;
|
||||
final int dex_version;
|
||||
|
||||
/**
|
||||
* read dex from a {@link ByteBuffer}.
|
||||
@ -134,8 +143,12 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
*/
|
||||
public DexFileReader(ByteBuffer in) {
|
||||
in.position(0);
|
||||
in = in.asReadOnlyBuffer().order(ByteOrder.LITTLE_ENDIAN);
|
||||
int magic = in.getInt() & 0x00FFFFFF;
|
||||
in = in.asReadOnlyBuffer().order(ByteOrder.BIG_ENDIAN);
|
||||
int magic = in.getInt() & 0xFFFFFF00;
|
||||
|
||||
final int MAGIC_DEX = 0x6465780A & 0xFFFFFF00;// hex for 'dex ', ignore the 0A
|
||||
final int MAGIC_ODEX = 0x6465790A & 0xFFFFFF00;// hex for 'dey ', ignore the 0A
|
||||
|
||||
if (magic == MAGIC_DEX) {
|
||||
;
|
||||
} else if (magic == MAGIC_ODEX) {
|
||||
@ -143,10 +156,12 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
} else {
|
||||
throw new DexException("not support magic.");
|
||||
}
|
||||
int version = in.getInt() & 0x00FFFFFF;
|
||||
if (version != MAGIC_035 && version != MAGIC_036) {
|
||||
int version = in.getInt() >> 8;
|
||||
if (version < 0 || version < DEX_035) {
|
||||
throw new DexException("not support version.");
|
||||
}
|
||||
this.dex_version = version;
|
||||
in.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
// skip uint checksum
|
||||
// and 20 bytes signature
|
||||
@ -161,14 +176,15 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
|
||||
// skip uint link_size
|
||||
// and uint link_off
|
||||
// and uint map_off
|
||||
skip(in, 4 + 4 + 4);
|
||||
skip(in, 4 + 4);
|
||||
|
||||
int map_off = in.getInt();
|
||||
|
||||
string_ids_size = in.getInt();
|
||||
int string_ids_off = in.getInt();
|
||||
type_ids_size = in.getInt();
|
||||
int type_ids_off = in.getInt();
|
||||
int proto_ids_size = in.getInt();
|
||||
proto_ids_size = in.getInt();
|
||||
int proto_ids_off = in.getInt();
|
||||
field_ids_size = in.getInt();
|
||||
int field_ids_off = in.getInt();
|
||||
@ -178,12 +194,43 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
int class_defs_off = in.getInt();
|
||||
// skip uint data_size data_off
|
||||
|
||||
int call_site_ids_off = 0;
|
||||
int call_site_ids_size = 0;
|
||||
int method_handle_ids_off = 0;
|
||||
int method_handle_ids_size = 0;
|
||||
if (dex_version > DEX_037) {
|
||||
in.position(map_off);
|
||||
int size = in.getInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
int type = in.getShort() & 0xFFFF;
|
||||
in.getShort(); // unused;
|
||||
int item_size = in.getInt();
|
||||
int item_offset = in.getInt();
|
||||
switch (type) {
|
||||
case TYPE_CALL_SITE_ID_ITEM:
|
||||
call_site_ids_off = item_offset;
|
||||
call_site_ids_size = item_size;
|
||||
break;
|
||||
case TYPE_METHOD_HANDLE_ITEM:
|
||||
method_handle_ids_off = item_offset;
|
||||
method_handle_ids_size = item_size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.call_site_ids_size = call_site_ids_size;
|
||||
this.method_handle_ids_size = method_handle_ids_size;
|
||||
|
||||
stringIdIn = slice(in, string_ids_off, string_ids_size * 4);
|
||||
typeIdIn = slice(in, type_ids_off, type_ids_size * 4);
|
||||
protoIdIn = slice(in, proto_ids_off, proto_ids_size * 12);
|
||||
fieldIdIn = slice(in, field_ids_off, field_ids_size * 8);
|
||||
methoIdIn = slice(in, method_ids_off, method_ids_size * 8);
|
||||
classDefIn = slice(in, class_defs_off, class_defs_size * 32);
|
||||
callSiteIdIn = slice(in, call_site_ids_off, call_site_ids_size * 4);
|
||||
methodHandleIdIn = slice(in, method_handle_ids_off, method_handle_ids_size * 8);
|
||||
|
||||
in.position(0);
|
||||
annotationsDirectoryItemIn = in.duplicate().order(ByteOrder.LITTLE_ENDIAN);
|
||||
@ -528,6 +575,11 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDexVersion() {
|
||||
return dex_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* equals to {@link #accept(DexFileVisitor, int)} with 0 as config
|
||||
*
|
||||
@ -626,47 +678,43 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
case VALUE_SHORT:
|
||||
return new Short((short) readIntBits(in, b));
|
||||
|
||||
case VALUE_CHAR:
|
||||
return new Character((char) readUIntBits(in, b));
|
||||
|
||||
case VALUE_INT:
|
||||
return new Integer((int) readIntBits(in, b));
|
||||
|
||||
case VALUE_LONG:
|
||||
return new Long(readIntBits(in, b));
|
||||
|
||||
case VALUE_CHAR:
|
||||
return new Character((char) readUIntBits(in, b));
|
||||
|
||||
case VALUE_STRING:
|
||||
return getString((int) readUIntBits(in, b));
|
||||
|
||||
case VALUE_FLOAT:
|
||||
return Float.intBitsToFloat((int) (readFloatBits(in, b) >> 32));
|
||||
|
||||
case VALUE_DOUBLE:
|
||||
return Double.longBitsToDouble(readFloatBits(in, b));
|
||||
case VALUE_METHOD_TYPE:
|
||||
return getProto((int) readUIntBits(in, b));
|
||||
case VALUE_METHOD_HANDLE:
|
||||
return getMethodHandle((int) readUIntBits(in, b));
|
||||
|
||||
case VALUE_NULL:
|
||||
return null;
|
||||
case VALUE_STRING:
|
||||
return getString((int) readUIntBits(in, b));
|
||||
|
||||
case VALUE_BOOLEAN: {
|
||||
return new Boolean(((b >> 5) & 0x3) != 0);
|
||||
|
||||
}
|
||||
case VALUE_TYPE: {
|
||||
int type_id = (int) readUIntBits(in, b);
|
||||
return new DexType(getType(type_id));
|
||||
}
|
||||
case VALUE_ENUM: {
|
||||
return getField((int) readUIntBits(in, b));
|
||||
case VALUE_FIELD: {
|
||||
int field_id = (int) readUIntBits(in, b);
|
||||
return getField(field_id);
|
||||
}
|
||||
|
||||
case VALUE_METHOD: {
|
||||
int method_id = (int) readUIntBits(in, b);
|
||||
return getMethod(method_id);
|
||||
|
||||
}
|
||||
case VALUE_FIELD: {
|
||||
int field_id = (int) readUIntBits(in, b);
|
||||
return getField(field_id);
|
||||
case VALUE_ENUM: {
|
||||
return getField((int) readUIntBits(in, b));
|
||||
}
|
||||
case VALUE_ARRAY: {
|
||||
return read_encoded_array(in);
|
||||
@ -674,11 +722,37 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
case VALUE_ANNOTATION: {
|
||||
return read_encoded_annotation(in);
|
||||
}
|
||||
case VALUE_NULL:
|
||||
return null;
|
||||
case VALUE_BOOLEAN: {
|
||||
return new Boolean(((b >> 5) & 0x3) != 0);
|
||||
}
|
||||
default:
|
||||
throw new DexException("Not support yet.");
|
||||
}
|
||||
}
|
||||
|
||||
private MethodHandle getMethodHandle(int i) {
|
||||
methodHandleIdIn.position(i * 8);
|
||||
int method_handle_type = methodHandleIdIn.getShort() & 0xFFFF;
|
||||
methodHandleIdIn.getShort();//unused
|
||||
int field_or_method_id = methodHandleIdIn.getShort() & 0xFFFF;
|
||||
|
||||
switch (method_handle_type) {
|
||||
case MethodHandle.INSTANCE_GET:
|
||||
case MethodHandle.INSTANCE_PUT:
|
||||
case MethodHandle.STATIC_GET:
|
||||
case MethodHandle.STATIC_PUT:
|
||||
return new MethodHandle(method_handle_type, getField(field_or_method_id));
|
||||
|
||||
case MethodHandle.INVOKE_INSTANCE:
|
||||
case MethodHandle.INVOKE_STATIC:
|
||||
return new MethodHandle(method_handle_type, getMethod(field_or_method_id));
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void acceptClass(DexClassVisitor dcv, int source_file_idx, int annotations_off, int class_data_off,
|
||||
int static_values_off, int config) {
|
||||
if ((config & SKIP_DEBUG) == 0) {
|
||||
@ -851,11 +925,7 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
return types;
|
||||
}
|
||||
|
||||
private Method getMethod(int id) {
|
||||
methoIdIn.position(id * 8);
|
||||
int owner_idx = 0xFFFF & methoIdIn.getShort();
|
||||
int proto_idx = 0xFFFF & methoIdIn.getShort();
|
||||
int name_idx = methoIdIn.getInt();
|
||||
private Proto getProto(int proto_idx) {
|
||||
String[] parameterTypes;
|
||||
String returnType;
|
||||
|
||||
@ -867,9 +937,15 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
returnType = getType(return_type_idx);
|
||||
|
||||
parameterTypes = getTypeList(parameters_off);
|
||||
return new Proto(parameterTypes, returnType);
|
||||
}
|
||||
|
||||
return new Method(getType(owner_idx), getString(name_idx), parameterTypes, returnType);
|
||||
|
||||
private Method getMethod(int id) {
|
||||
methoIdIn.position(id * 8);
|
||||
int owner_idx = 0xFFFF & methoIdIn.getShort();
|
||||
int proto_idx = 0xFFFF & methoIdIn.getShort();
|
||||
int name_idx = methoIdIn.getInt();
|
||||
return new Method(getType(owner_idx), getString(name_idx), getProto(proto_idx));
|
||||
}
|
||||
|
||||
private String getString(int id) {
|
||||
@ -1197,6 +1273,15 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
idx = ushort(insns, u1offset + 2);
|
||||
canContinue = idx < field_ids_size;
|
||||
break;
|
||||
case kIndexCallSiteRef:
|
||||
idx = ushort(insns, u1offset + 2);
|
||||
canContinue = idx < call_site_ids_size;
|
||||
break;
|
||||
case kIndexMethodAndProtoRef:
|
||||
idx = ushort(insns, u1offset + 2);
|
||||
int idx2 = ushort(insns, u1offset + 6);
|
||||
canContinue = idx < method_ids_size && idx2 < proto_ids_size;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
if (!canContinue) {
|
||||
@ -1537,6 +1622,10 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
}
|
||||
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
|
||||
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
|
||||
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
|
||||
Object[] callsite = getCallSite(b);
|
||||
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length);
|
||||
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
|
||||
} else {
|
||||
dcv.visitMethodStmt(op, regs, getMethod(b));
|
||||
}
|
||||
@ -1552,11 +1641,50 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
}
|
||||
if (op.indexType == InstructionIndexType.kIndexTypeRef) {
|
||||
dcv.visitFilledNewArrayStmt(op, regs, getType(b));
|
||||
} else if (op.indexType == InstructionIndexType.kIndexCallSiteRef) {
|
||||
Object[] callsite = getCallSite(b);
|
||||
Object[] constArgs = Arrays.copyOfRange(callsite, 3, callsite.length - 3);
|
||||
dcv.visitMethodStmt(op, regs, (String) callsite[1], (Proto) callsite[2], (MethodHandle) callsite[0], constArgs);
|
||||
} else {
|
||||
dcv.visitMethodStmt(op, regs, getMethod(b));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kFmt45cc: {
|
||||
a = ubyte(insns, u1offset + 1);
|
||||
b = ushort(insns, u1offset + 2);
|
||||
int dc = ubyte(insns, u1offset + 4); // DC
|
||||
int fe = ubyte(insns, u1offset + 5); // FE
|
||||
int h = ushort(insns, u1offset + 6);
|
||||
|
||||
int regs[] = new int[a >> 4];
|
||||
switch (a >> 4) {
|
||||
case 5:
|
||||
regs[4] = a & 0xF;// G
|
||||
case 4:
|
||||
regs[3] = 0xF & (fe >> 4);// F
|
||||
case 3:
|
||||
regs[2] = 0xF & (fe >> 0);// E
|
||||
case 2:
|
||||
regs[1] = 0xF & (dc >> 4);// D
|
||||
case 1:
|
||||
regs[0] = 0xF & (dc >> 0);// C
|
||||
}
|
||||
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
|
||||
}
|
||||
break;
|
||||
case kFmt4rcc: {
|
||||
a = ubyte(insns, u1offset + 1);
|
||||
b = ushort(insns, u1offset + 2);
|
||||
c = ushort(insns, u1offset + 4);
|
||||
int h = ushort(insns, u1offset + 6);
|
||||
int regs[] = new int[a];
|
||||
for (int i = 0; i < a; i++) {
|
||||
regs[i] = c + i;
|
||||
}
|
||||
dcv.visitMethodStmt(op, regs, getMethod(b), getProto(h));
|
||||
}
|
||||
break;
|
||||
case kFmt22x:
|
||||
a = ubyte(insns, u1offset + 1);
|
||||
b = ushort(insns, u1offset + 2);
|
||||
@ -1638,6 +1766,13 @@ public class DexFileReader implements BaseDexFileReader {
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] getCallSite(int b) {
|
||||
callSiteIdIn.position(b * 4);
|
||||
int call_site_off = callSiteIdIn.getInt();
|
||||
|
||||
return read_encoded_array_item(call_site_off);
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in the resulting locals table
|
||||
*/
|
||||
|
@ -70,6 +70,18 @@ public class MultiDexFileReader implements BaseDexFileReader {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDexVersion() {
|
||||
int max = DEX_035;
|
||||
for (DexFileReader r : readers) {
|
||||
int v = r.getDexVersion();
|
||||
if (v > max) {
|
||||
max = v;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DexFileVisitor dv) {
|
||||
accept(dv, 0);
|
||||
|
@ -18,10 +18,7 @@ package com.googlecode.d2j.util;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.googlecode.d2j.DexConstants;
|
||||
import com.googlecode.d2j.DexLabel;
|
||||
import com.googlecode.d2j.Field;
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.*;
|
||||
import com.googlecode.d2j.reader.Op;
|
||||
import com.googlecode.d2j.visitors.DexCodeVisitor;
|
||||
import com.googlecode.d2j.visitors.DexDebugVisitor;
|
||||
@ -137,6 +134,16 @@ public class ASMifierCodeV extends DexCodeVisitor implements DexConstants {
|
||||
m.s("code.visitJumpStmt(%s,%s,%s,%s);", op(op), a, b, v(label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
|
||||
m.s("code.visitMethodStmt(%s,%s,%s,%s,%s,%s);", op(op), Escape.v(args), Escape.v(name), Escape.v(proto), Escape.v(bsm), Escape.v(bsmArgs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
|
||||
m.s("code.visitMethodStmt(%s,%s,%s,%s);", op(op), Escape.v(args), Escape.v(bsm), Escape.v(proto));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodStmt(Op op, int[] args, Method method) {
|
||||
m.s("code.visitMethodStmt(%s,%s,%s);", op(op), Escape.v(args), Escape.v(method));
|
||||
|
@ -15,10 +15,7 @@
|
||||
*/
|
||||
package com.googlecode.d2j.util;
|
||||
|
||||
import com.googlecode.d2j.DexConstants;
|
||||
import com.googlecode.d2j.DexType;
|
||||
import com.googlecode.d2j.Field;
|
||||
import com.googlecode.d2j.Method;
|
||||
import com.googlecode.d2j.*;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:pxb1988@gmail.com">Panxiaobo</a>
|
||||
@ -165,6 +162,29 @@ public class Escape implements DexConstants {
|
||||
return String.format("new Method(%s,%s,%s,%s)", v(m.getOwner()), v(m.getName()), v(m.getParameterTypes()),
|
||||
v(m.getReturnType()));
|
||||
}
|
||||
public static String v(Proto m) {
|
||||
return String.format("new Proto(%s,%s)", v(m.getParameterTypes()), v(m.getReturnType()));
|
||||
}
|
||||
|
||||
public static String v(MethodHandle m) {
|
||||
switch (m.getType()) {
|
||||
case MethodHandle.INSTANCE_GET:
|
||||
return String.format("new MethodHandle(MethodHandle.INSTANCE_GET,%s)", v(m.getField()));
|
||||
case MethodHandle.INSTANCE_PUT:
|
||||
return String.format("new MethodHandle(MethodHandle.INSTANCE_PUT,%s)", v(m.getField()));
|
||||
case MethodHandle.STATIC_GET:
|
||||
return String.format("new MethodHandle(MethodHandle.STATIC_GET,%s)", v(m.getField()));
|
||||
case MethodHandle.STATIC_PUT:
|
||||
return String.format("new MethodHandle(MethodHandle.STATIC_PUT,%s)", v(m.getField()));
|
||||
|
||||
case MethodHandle.INVOKE_INSTANCE:
|
||||
return String.format("new MethodHandle(MethodHandle.INVOKE_INSTANCE,%s)", v(m.getMethod()));
|
||||
case MethodHandle.INVOKE_STATIC:
|
||||
return String.format("new MethodHandle(MethodHandle.INVOKE_STATIC,%s)", v(m.getMethod()));
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public static String v(String s) {
|
||||
if (s == null) {
|
||||
@ -255,6 +275,12 @@ public class Escape implements DexConstants {
|
||||
if (obj instanceof Field) {
|
||||
return v((Field) obj);
|
||||
}
|
||||
if (obj instanceof Proto) {
|
||||
return v((Proto) obj);
|
||||
}
|
||||
if (obj instanceof MethodHandle) {
|
||||
return v((MethodHandle) obj);
|
||||
}
|
||||
|
||||
if (obj instanceof Integer) {
|
||||
return " Integer.valueOf(" + obj + ")";
|
||||
|
@ -423,21 +423,21 @@ public class DecryptStringCmd extends BaseCmd {
|
||||
if (op.vt == Value.VT.INVOKE_STATIC) {
|
||||
InvokeExpr ie = (InvokeExpr) op;
|
||||
MethodConfig key = DecryptStringCmd.this.key;
|
||||
key.owner = ie.owner.substring(1, ie.owner.length() - 1);
|
||||
key.name = ie.name;
|
||||
key.desc = buildMethodDesc(ie.args, ie.ret);
|
||||
key.owner = ie.getOwner().substring(1, ie.getOwner().length() - 1);
|
||||
key.name = ie.getName();
|
||||
key.desc = buildMethodDesc(ie.getArgs(), ie.getRet());
|
||||
|
||||
MethodConfig c = map.get(key);
|
||||
if (c != null) {
|
||||
try {
|
||||
Method jmethod = c.jmethod;
|
||||
if (ie.args.length != jmethod.getParameterTypes().length) {
|
||||
if (ie.getArgs().length != jmethod.getParameterTypes().length) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
Object args[] = new Object[ie.args.length];
|
||||
Object args[] = new Object[ie.getArgs().length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = convertIr2Jobj(ie.getOps()[i], ie.args[i]);
|
||||
args[i] = convertIr2Jobj(ie.getOps()[i], ie.getArgs()[i]);
|
||||
}
|
||||
if (verbose) {
|
||||
System.out.println(" > calling " + jmethod + " with arguments " + v(args));
|
||||
|
@ -1115,15 +1115,44 @@ public class Dex2IRConverter {
|
||||
}
|
||||
|
||||
return value;
|
||||
case INVOKE_CUSTOM:
|
||||
case INVOKE_CUSTOM_RANGE: {
|
||||
Value[] vs = new Value[values.size()];
|
||||
for (int i = 0; i < vs.length; i++) {
|
||||
vs[i] = getLocal(values.get(i));
|
||||
}
|
||||
MethodCustomStmtNode n = (MethodCustomStmtNode) insn;
|
||||
Value invoke = nInvokeCustom(vs, n.name, n.proto, n.bsm, n.bsmArgs);
|
||||
if ("V".equals(n.getProto().getReturnType())) {
|
||||
emit(nVoidInvoke(invoke));
|
||||
return null;
|
||||
} else {
|
||||
return b(invoke);
|
||||
}
|
||||
}
|
||||
case INVOKE_POLYMORPHIC:
|
||||
case INVOKE_POLYMORPHIC_RANGE: {
|
||||
Value[] vs = new Value[values.size()];
|
||||
for (int i = 0; i < vs.length; i++) {
|
||||
vs[i] = getLocal(values.get(i));
|
||||
}
|
||||
MethodPolymorphicStmtNode n = (MethodPolymorphicStmtNode) insn;
|
||||
Value invoke = nInvokePolymorphic(vs, n.proto, n.method);
|
||||
if ("V".equals(n.getProto().getReturnType())) {
|
||||
emit(nVoidInvoke(invoke));
|
||||
return null;
|
||||
} else {
|
||||
return b(invoke);
|
||||
}
|
||||
}
|
||||
default:
|
||||
Op op = insn.op;
|
||||
Method method = ((MethodStmtNode) insn).method;
|
||||
Value[] vs = new Value[values.size()];
|
||||
for (int i = 0; i < vs.length; i++) {
|
||||
vs[i] = getLocal(values.get(i));
|
||||
}
|
||||
|
||||
|
||||
Method method = ((MethodStmtNode) insn).method;
|
||||
Value invoke = null;
|
||||
switch (op) {
|
||||
case INVOKE_VIRTUAL_RANGE:
|
||||
|
@ -16,7 +16,9 @@
|
||||
*/
|
||||
package com.googlecode.d2j.converter;
|
||||
|
||||
import com.googlecode.d2j.DexType;
|
||||
import com.googlecode.d2j.asm.LdcOptimizeAdapter;
|
||||
import com.googlecode.d2j.dex.Dex2Asm;
|
||||
import com.googlecode.dex2jar.ir.IrMethod;
|
||||
import com.googlecode.dex2jar.ir.Trap;
|
||||
import com.googlecode.dex2jar.ir.expr.*;
|
||||
@ -27,10 +29,7 @@ import com.googlecode.dex2jar.ir.expr.Value.VT;
|
||||
import com.googlecode.dex2jar.ir.stmt.*;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.E2Stmt;
|
||||
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.HashMap;
|
||||
@ -635,13 +634,13 @@ public class IR2JConverter implements Opcodes {
|
||||
asm.visitMultiANewArrayInsn(sb.toString(), value.ops.length);
|
||||
break;
|
||||
case INVOKE_NEW:
|
||||
asm.visitTypeInsn(NEW, toInternal(((InvokeExpr) value).owner));
|
||||
asm.visitTypeInsn(NEW, toInternal(((InvokeExpr) value).getOwner()));
|
||||
asm.visitInsn(DUP);
|
||||
// pass through
|
||||
case INVOKE_INTERFACE:
|
||||
case INVOKE_SPECIAL:
|
||||
case INVOKE_STATIC:
|
||||
case INVOKE_VIRTUAL:
|
||||
case INVOKE_VIRTUAL: {
|
||||
InvokeExpr ie = (InvokeExpr) value;
|
||||
int i = 0;
|
||||
if (value.vt != VT.INVOKE_STATIC && value.vt != VT.INVOKE_NEW) {
|
||||
@ -651,7 +650,7 @@ public class IR2JConverter implements Opcodes {
|
||||
for (int j = 0; i < value.ops.length; i++, j++) {
|
||||
Value vb = value.ops[i];
|
||||
accept(vb, asm);
|
||||
insertI2x(vb.valueType, ie.args[j], asm);
|
||||
insertI2x(vb.valueType, ie.getArgs()[j], asm);
|
||||
}
|
||||
|
||||
int opcode;
|
||||
@ -673,9 +672,36 @@ public class IR2JConverter implements Opcodes {
|
||||
opcode = -1;
|
||||
}
|
||||
|
||||
asm.visitMethodInsn(opcode, toInternal(ie.owner), ie.name,
|
||||
buildMethodDesc(ie.vt == VT.INVOKE_NEW ? "V" : ie.ret, ie.args));
|
||||
break;
|
||||
asm.visitMethodInsn(opcode, toInternal(ie.getOwner()), ie.getName(),
|
||||
buildMethodDesc(ie.vt == VT.INVOKE_NEW ? "V" : ie.getRet(), ie.getArgs()));
|
||||
}
|
||||
break;
|
||||
case INVOKE_CUSTOM: {
|
||||
InvokeCustomExpr ice = (InvokeCustomExpr) value;
|
||||
String argTypes[] = ice.getProto().getParameterTypes();
|
||||
Value[] vbs = ice.getOps();
|
||||
if (argTypes.length == vbs.length) {
|
||||
for (int i = 0; i < vbs.length; i++) {
|
||||
Value vb = vbs[i];
|
||||
accept(vb, asm);
|
||||
insertI2x(vb.valueType, argTypes[i], asm);
|
||||
}
|
||||
} else if (argTypes.length + 1 == vbs.length) {
|
||||
accept(vbs[0], asm);
|
||||
for (int i = 1; i < vbs.length; i++) {
|
||||
Value vb = vbs[i];
|
||||
accept(vb, asm);
|
||||
insertI2x(vb.valueType, argTypes[i - 1], asm);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
asm.visitInvokeDynamicInsn(ice.name, ice.proto.getDesc(), (Handle) Dex2Asm.convertConstantValue(ice.handle), Dex2Asm.convertConstantValues(ice.bsmArgs));
|
||||
}
|
||||
break;
|
||||
case INVOKE_POLYMORPHIC: {
|
||||
throw new RuntimeException("INVOKE_POLYMORPHIC"); // auto box/unbox ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,10 +473,7 @@ public class Dex2Asm {
|
||||
}
|
||||
}
|
||||
}
|
||||
Object value = fieldNode.cst;
|
||||
if (value instanceof DexType) {
|
||||
value = Type.getType(((DexType) value).desc);
|
||||
}
|
||||
Object value = convertConstantValue(fieldNode.cst);
|
||||
final int FieldCleanFlag = ~DexConstants.ACC_DECLARED_SYNCHRONIZED;
|
||||
FieldVisitor fv = cv.visitField(fieldNode.access & FieldCleanFlag, fieldNode.field.getName(),
|
||||
fieldNode.field.getType(), signature, value);
|
||||
@ -487,6 +484,47 @@ public class Dex2Asm {
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
public static Object[] convertConstantValues(Object[] v) {
|
||||
Object[] copy = Arrays.copyOf(v, v.length);
|
||||
for (int i = 0; i < copy.length; i++) {
|
||||
Object ele = copy[i];
|
||||
ele = convertConstantValue(ele);
|
||||
copy[i] = ele;
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static Object convertConstantValue(Object ele) {
|
||||
if (ele instanceof DexType) {
|
||||
ele = Type.getType(((DexType) ele).desc);
|
||||
} else if (ele instanceof MethodHandle) {
|
||||
Handle h = null;
|
||||
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());
|
||||
break;
|
||||
case MethodHandle.INSTANCE_PUT:
|
||||
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
|
||||
break;
|
||||
case MethodHandle.STATIC_GET:
|
||||
h = new Handle(Opcodes.H_GETFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
|
||||
break;
|
||||
case MethodHandle.STATIC_PUT:
|
||||
h = new Handle(Opcodes.H_PUTFIELD, toInternalName(mh.getField().getOwner()), mh.getField().getName(), mh.getField().getType());
|
||||
break;
|
||||
case MethodHandle.INVOKE_INSTANCE:
|
||||
h = new Handle(Opcodes.H_INVOKEVIRTUAL, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
|
||||
break;
|
||||
case MethodHandle.INVOKE_STATIC:
|
||||
h = new Handle(Opcodes.H_INVOKESTATIC, toInternalName(mh.getMethod().getOwner()), mh.getMethod().getName(), mh.getMethod().getDesc());
|
||||
break;
|
||||
}
|
||||
ele = h;
|
||||
}
|
||||
return ele;
|
||||
}
|
||||
|
||||
public void convertMethod(DexClassNode classNode, DexMethodNode methodNode, ClassVisitor cv) {
|
||||
|
||||
MethodVisitor mv = collectBasicMethodInfo(methodNode, cv);
|
||||
|
Loading…
Reference in New Issue
Block a user