support dex 038

This commit is contained in:
Bob Pan 2023-08-31 12:55:00 +08:00
parent caf241d635
commit 0862aaeb39
26 changed files with 599 additions and 123 deletions

View File

@ -175,8 +175,7 @@ sAnnotationValue
:sSubannotation
|sBaseValue
|sArrayValue
| ( '.iget' | '.iput' | '.sget' | '.sput' ) FIELD_FULL
| ( '.invoke-instance' | '.invoke-static' ) METHOD_FULL
| method_handler
;// field,method,array,subannotation
sBaseValue
:STRING
@ -199,6 +198,11 @@ method_handler
| type=('invoke-static'|'invoke-instance'|'invoke-direct'|'invoke-interface'|'invoke-constructor') '@' mtd=METHOD_FULL
;
// FIXME samli syntax only write out method_handler's method field
call_site
: name=sAnnotationKeyName '(' method_name=STRING ',' method_type=METHOD_PROTO (',' sBaseValue)* ')' '@' bsm=METHOD_FULL
;
sInstruction
:fline
|flocal
@ -415,9 +419,9 @@ fm45cc : op='invoke-polymorphic' '{' (REGISTER (',' REGISTER)* )? '}' ',' metho
;
fm4rcc : op='invoke-polymorphic/range' '{' (rstart=REGISTER '..' rend=REGISTER)? '}' ',' method=METHOD_FULL ',' proto=METHOD_PROTO
;
fmcustomc : op='invoke-custom' '{' (REGISTER (',' REGISTER)* )? '}' ',' sArrayValue
fmcustomc : op='invoke-custom' '{' (REGISTER (',' REGISTER)* )? '}' ',' call_site
;
fmcustomrc : op='invoke-custom/range' '{' (rstart=REGISTER '..' rend=REGISTER)? '}' ',' sArrayValue
fmcustomrc : op='invoke-custom/range' '{' (rstart=REGISTER '..' rend=REGISTER)? '}' ',' call_site
;
ftrc : op='filled-new-array/range' '{' (rstart=REGISTER '..' rend=REGISTER)? '}' ',' type=(OBJECT_TYPE|ARRAY_TYPE);
f31t: op=('fill-array-data'|'packed-switch'|'sparse-switch') r1=REGISTER ',' label=LABEL;

View File

@ -431,6 +431,65 @@ public class AntlrSmaliUtil {
return null;
}
@Override
public Object visitFm4rcc(SmaliParser.Fm4rccContext ctx) {
if (ctx.rstart != null) {
int start = m.pareReg(ctx.rstart.getText());
int end = m.pareReg(ctx.rend.getText());
int size = end - start + 1;
int[] rs = new int[size];
for (int i = 0; i < size; i++) {
rs[i] = start + i;
}
scv.visitMethodStmt(getOp(ctx.op), rs, parseMethodAndUnescape(ctx.method.getText()), parseProtoAndUnescape(ctx.proto.getText()));
} else {
scv.visitMethodStmt(getOp(ctx.op), new int[0], parseMethodAndUnescape(ctx.method.getText()), parseProtoAndUnescape(ctx.proto.getText()));
}
return null;
}
@Override
public Object visitFm45cc(SmaliParser.Fm45ccContext ctx) {
Op op = getOp(ctx.op);
List<TerminalNode> ts = ctx.REGISTER();
int[] rs = new int[ts.size()];
for (int i = 0; i < ts.size(); i++) {
rs[i] = m.pareReg(ts.get(i).getSymbol().getText());
}
scv.visitMethodStmt(op, rs, parseMethodAndUnescape(ctx.method.getText()), parseProtoAndUnescape(ctx.proto.getText()));
return null;
}
@Override
public Object visitFmcustomc(SmaliParser.FmcustomcContext ctx) {
Op op = getOp(ctx.op);
List<TerminalNode> ts = ctx.REGISTER();
int[] rs = new int[ts.size()];
for (int i = 0; i < ts.size(); i++) {
rs[i] = m.pareReg(ts.get(i).getSymbol().getText());
}
scv.visitMethodStmt(op, rs, parseCallSite(ctx.call_site()));
return null;
}
@Override
public Object visitFmcustomrc(SmaliParser.FmcustomrcContext ctx) {
if (ctx.rstart != null) {
int start = m.pareReg(ctx.rstart.getText());
int end = m.pareReg(ctx.rend.getText());
int size = end - start + 1;
int[] rs = new int[size];
for (int i = 0; i < size; i++) {
rs[i] = start + i;
}
scv.visitMethodStmt(getOp(ctx.op), rs, parseCallSite(ctx.call_site()));
} else {
scv.visitMethodStmt(getOp(ctx.op), new int[0], parseCallSite(ctx.call_site()));
}
return null;
}
@Override
public Object visitFmrc(SmaliParser.FmrcContext ctx) {
if (ctx.rstart != null) {
@ -554,6 +613,25 @@ public class AntlrSmaliUtil {
scv.visitEnd();
}
private static CallSite parseCallSite(SmaliParser.Call_siteContext callSiteContext) {
List<SmaliParser.SBaseValueContext> sBaseValueContexts = callSiteContext.sBaseValue();
Object[] args = new Object[sBaseValueContexts.size()];
int i = 0;
for (SmaliParser.SBaseValueContext baseValueContext : sBaseValueContexts) {
args[i] = parseBaseValue(baseValueContext);
i++;
}
return new CallSite(
unEscapeId(callSiteContext.name.getText()),
new MethodHandle(MethodHandle.INVOKE_STATIC, parseMethodAndUnescape(callSiteContext.bsm.getText())),
unescapeStr(callSiteContext.method_name.getText()),
parseProtoAndUnescape(callSiteContext.method_type.getText()),
args
);
}
private static MethodHandle parseMethodHandler(SmaliParser.Method_handlerContext methodHandlerContext) {
MethodHandle value;
switch (methodHandlerContext.type.getText()) {
@ -781,6 +859,10 @@ public class AntlrSmaliUtil {
Object value = parseBaseValue(baseValueContext);
dexAnnotationVisitor.visit(name, value);
break;
case SmaliParser.RULE_method_handler:
MethodHandle methodHandle = parseMethodHandler((SmaliParser.Method_handlerContext) t);
dexAnnotationVisitor.visit(name, methodHandle);
break;
}
}

View File

@ -385,14 +385,25 @@ import java.util.*;
}
@Override
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
public void visitMethodStmt(Op op, int[] args, CallSite callSite) {
StringBuilder sb = new StringBuilder();
sb.append("{ ").append( BaksmaliDumper.escapeValue(bsm)).append(", ").append(BaksmaliDumper.escapeValue(name)).append(", ").append(BaksmaliDumper.escapeMethodDesc(proto));
for(Object o: bsmArgs) {
sb.append(", ").append(BaksmaliDumper.escapeValue(o));
}
sb.append("}");
Object[] extraArguments = callSite.getExtraArguments();
// invoke-custom/range {v0 .. v5}, call_site_1("runDynamic", (IIIIII)V, 0x378)@L038;->bsm(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;
sb
.append(BaksmaliDumper.escapeId(callSite.getName()))
.append('(')
.append(BaksmaliDumper.escapeValue(callSite.getMethodName()))
.append(", ")
.append(BaksmaliDumper.escapeMethodDesc(callSite.getMethodProto()));
if (extraArguments != null && extraArguments.length > 0) {
for (Object o : extraArguments) {
sb.append(", ").append(BaksmaliDumper.escapeValue(o));
}
}
// FIXME samli syntax only write out method_handler's method field
sb.append(")@").append(BaksmaliDumper.escapeMethod(callSite.getBootstrapMethodHandler().getMethod()));
if (args.length > 0) {
if (op.format == InstructionFormat.kFmt3rc) { // invoke-x/range

View File

@ -72,14 +72,7 @@ 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);
}
Opcodes opcodes = Opcodes.forApi(DexConstants.toMiniAndroidApiLevel(dexVersion));
DexBackedDexFile dex;
try {
dex = DexFileFactory.loadDexFile(dexFile, opcodes);

View File

@ -0,0 +1,37 @@
package com.googlecode.d2j;
public class CallSite {
private String name;
private MethodHandle bootstrapMethodHandler;
private String methodName;
private Proto methodProto;
private Object[] extraArguments;
public CallSite(String name, MethodHandle bootstrapMethodHandler, String methodName, Proto methodProto, Object... extraArguments) {
this.name = name;
this.bootstrapMethodHandler = bootstrapMethodHandler;
this.methodName = methodName;
this.methodProto = methodProto;
this.extraArguments = extraArguments;
}
public String getName() {
return name;
}
public MethodHandle getBootstrapMethodHandler() {
return bootstrapMethodHandler;
}
public String getMethodName() {
return methodName;
}
public Proto getMethodProto() {
return methodProto;
}
public Object[] getExtraArguments() {
return extraArguments;
}
}

View File

@ -52,10 +52,25 @@ public abstract interface DexConstants {
String ANNOTATION_INNER_CLASS_TYPE = "Ldalvik/annotation/InnerClass;";
String ANNOTATION_MEMBER_CLASSES_TYPE = "Ldalvik/annotation/MemberClasses;";
static int toMiniAndroidApiLevel(int dexVersion) {
if (dexVersion <= DEX_035 || dexVersion <= DEX_036) {
return 0;
} else if (dexVersion == DEX_037) {
return 24;
} else if (dexVersion == DEX_038) {
return 26;
} else {
return 28;
}
}
int DEX_035 = 0x00303335;
@Deprecated
int DEX_036 = 0x00303336;
// android 7.0, api 24
int DEX_037 = 0x00303337;
// android 8.0, api 26
int DEX_038 = 0x00303338;
// android 9.0, api 28
int DEX_039 = 0x00303339;
int DEX_040 = 0x00303340;
}

View File

@ -110,8 +110,8 @@ public class DexCodeNode extends DexCodeVisitor {
}
@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));
public void visitMethodStmt(Op op, int[] args, CallSite callSite) {
add(new MethodCustomStmtNode(op, args, callSite));
}
@Override

View File

@ -15,32 +15,26 @@
*/
package com.googlecode.d2j.node.insn;
import com.googlecode.d2j.MethodHandle;
import com.googlecode.d2j.CallSite;
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 final CallSite callSite;
public MethodCustomStmtNode(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object[] bsmArgs) {
public MethodCustomStmtNode(Op op, int[] args, CallSite callSite) {
super(op, args);
this.proto = proto;
this.name = name;
this.bsm = bsm;
this.bsmArgs = bsmArgs;
this.callSite = callSite;
}
@Override
public void accept(DexCodeVisitor cv) {
cv.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
cv.visitMethodStmt(op, args, callSite);
}
@Override
public Proto getProto() {
return proto;
return callSite.getMethodProto();
}
}

View File

@ -238,9 +238,9 @@ public class DexCodeVisitor {
* OP_INVOKE_CUSTOM
* </pre>
*/
public void visitMethodStmt(Op op, int[] args, String name, Proto proto, MethodHandle bsm, Object... bsmArgs) {
public void visitMethodStmt(Op op, int[] args, CallSite callSite) {
if (visitor != null) {
visitor.visitMethodStmt(op, args, name, proto, bsm, bsmArgs);
visitor.visitMethodStmt(op, args, callSite);
}
}

View File

@ -205,7 +205,7 @@ public class DexFileReader implements BaseDexFileReader {
int call_site_ids_size = 0;
int method_handle_ids_off = 0;
int method_handle_ids_size = 0;
if (dex_version > DEX_037) {
if (dex_version >= DEX_038) {
in.position(map_off);
int size = in.getInt();
for (int i = 0; i < size; i++) {
@ -1642,9 +1642,7 @@ 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);
dcv.visitMethodStmt(op, regs, getCallSite(b));
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
@ -1661,9 +1659,7 @@ 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);
dcv.visitMethodStmt(op, regs, getCallSite(b));
} else {
dcv.visitMethodStmt(op, regs, getMethod(b));
}
@ -1785,11 +1781,24 @@ public class DexFileReader implements BaseDexFileReader {
}
}
private Object[] getCallSite(int b) {
private CallSite getCallSite(int b) {
callSiteIdIn.position(b * 4);
int call_site_off = callSiteIdIn.getInt();
return read_encoded_array_item(call_site_off);
Object[] call_site_items = read_encoded_array_item(call_site_off);
Object[] constArgs;
if (call_site_items.length > 3) {
constArgs = Arrays.copyOfRange(call_site_items, 3, call_site_items.length);
} else {
constArgs = new Object[0];
}
return new CallSite(
String.format("call_site_%d", b),
(MethodHandle) call_site_items[0],
(String) call_site_items[1],
(Proto) call_site_items[2],
constArgs);
}
/**

View File

@ -135,8 +135,8 @@ public class ASMifierCodeV extends DexCodeVisitor implements DexConstants {
}
@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));
public void visitMethodStmt(Op op, int[] args, CallSite callSite) {
m.s("code.visitMethodStmt(%s,%s,%s);", op(op), Escape.v(args), Escape.v(callSite));
}
@Override

View File

@ -263,6 +263,26 @@ public class Escape implements DexConstants {
return sb.append("}").toString();
}
public static String v(CallSite callSite) {
StringBuilder sb = new StringBuilder()
.append("new CallSite(")
.append(v(callSite.getName()))
.append(", ")
.append(v(callSite.getBootstrapMethodHandler()))
.append(", ")
.append(v(callSite.getMethodName()))
.append(", ")
.append(v(callSite.getMethodProto()));
Object[] extraArguments = callSite.getExtraArguments();
if (extraArguments != null && extraArguments.length > 0) {
for (Object arg : extraArguments) {
sb.append(", ").append(v(arg));
}
}
sb.append(")");
return sb.toString();
}
public static String v(Object obj) {
if (obj == null) {
return "null";
@ -287,6 +307,9 @@ public class Escape implements DexConstants {
if (obj instanceof MethodHandle) {
return v((MethodHandle) obj);
}
if (obj instanceof CallSite) {
return v((CallSite) obj);
}
if (obj instanceof Integer) {
return " Integer.valueOf(" + obj + ")";

View File

@ -1135,7 +1135,11 @@ public class Dex2IRConverter {
vs[i] = getLocal(values.get(i));
}
MethodCustomStmtNode n = (MethodCustomStmtNode) insn;
Value invoke = nInvokeCustom(vs, n.name, n.proto, n.bsm, n.bsmArgs);
Value invoke = nInvokeCustom(vs,
n.callSite.getMethodName(),
n.callSite.getMethodProto(),
n.callSite.getBootstrapMethodHandler(),
n.callSite.getExtraArguments());
if ("V".equals(n.getProto().getReturnType())) {
emit(nVoidInvoke(invoke));
return null;

View File

@ -47,12 +47,10 @@ import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicVerifier;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
@ -329,11 +327,7 @@ public abstract class TestUtils {
cfOptions.strictNameCheck = false;
DexOptions dexOptions = new DexOptions();
if (fileNode != null) {
if (fileNode.dexVersion >= DexConstants.DEX_039) {
dexOptions.minSdkVersion = 28;
} else if (fileNode.dexVersion >= DexConstants.DEX_037) {
dexOptions.minSdkVersion = 26;
}
dexOptions.minSdkVersion = DexConstants.toMiniAndroidApiLevel(fileNode.dexVersion);
}
DirectClassFile dcf = new DirectClassFile(data, rca.getClassName() + ".class", true);

Binary file not shown.

View File

@ -16,9 +16,11 @@
*/
package com.googlecode.d2j.dex.writer;
import com.googlecode.d2j.CallSite;
import com.googlecode.d2j.DexLabel;
import com.googlecode.d2j.Field;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.Proto;
import com.googlecode.d2j.dex.writer.insn.*;
import com.googlecode.d2j.dex.writer.item.*;
import com.googlecode.d2j.reader.Op;
@ -390,6 +392,9 @@ public class CodeWriter extends DexCodeVisitor {
*/
@Override
public void visitConstStmt(Op op, int ra, Object value) {
if (op == CONST_METHOD_HANDLE || op == CONST_METHOD_TYPE) {
cp.dex039();
}
switch (op.format) {
case kFmt21c:// value is field,type,string,method_handle,proto
case kFmt31c:// value is string,
@ -494,6 +499,33 @@ public class CodeWriter extends DexCodeVisitor {
}
}
@Override
public void visitMethodStmt(Op op, int[] args, CallSite callSite) {
cp.dex038();
if (op.format == kFmt3rc) {
ops.add(new CodeWriter.OP3rc(op, args, cp.uniqCallSite(callSite)));
} else if (op.format == kFmt35c) {
ops.add(new CodeWriter.OP35c(op, args, cp.uniqCallSite(callSite)));
}
if (args.length > max_out_reg_size) {
max_out_reg_size = args.length;
}
}
@Override
public void visitMethodStmt(Op op, int[] args, Method bsm, Proto proto) {
cp.dex038();
if (op.format == kFmt4rcc) {
ops.add(new CodeWriter.OP4rcc(op, args, cp.uniqMethod(bsm), cp.uniqProto(proto)));
} else if (op.format == kFmt45cc) {
ops.add(new CodeWriter.OP45cc(op, args, cp.uniqMethod(bsm), cp.uniqProto(proto)));
}
if (args.length > max_out_reg_size) {
max_out_reg_size = args.length;
}
}
@Override
public void visitPackedSwitchStmt(Op op, int aA, final int first_case, final DexLabel[] labels) {
Label switch_data_location = new Label();
@ -750,6 +782,95 @@ public class CodeWriter extends DexCodeVisitor {
}
}
//A|G|op BBBB F|E|D|C HHHH
public static class OP45cc extends OpInsn {
final BaseItem mtd;
final BaseItem proto;
int A, C, D, E, F, G;
public OP45cc(Op op, int[] args, BaseItem mtd, BaseItem proto) {
super(op);
int A = args.length;
if (A > 5) {
throw new CantNotFixContentException(op, "A", A);
}
this.A = A;
switch (A) { // [A=5] op {vC, vD, vE, vF, vG},
case 5:
G = args[4];
checkContentU4bit(op, "vG", G);
case 4:
F = args[3];
checkContentU4bit(op, "vF", F);
case 3:
E = args[2];
checkContentU4bit(op, "vE", E);
case 2:
D = args[1];
checkContentU4bit(op, "vD", D);
case 1:
C = args[0];
checkContentU4bit(op, "vC", C);
break;
}
this.mtd = mtd;
this.proto = proto;
}
@Override
public void write(ByteBuffer out) { // A|G|op BBBB F|E|D|C HHHH
checkContentUShort(op, "@BBBB", mtd.index);
checkContentUShort(op, "@HHHH", proto.index);
out
.put((byte) op.opcode).put((byte) ((A << 4) | (G & 0xF))) //
.putShort((short) mtd.index) //
.put((byte) ((D << 4) | (C & 0xF))).put((byte) ((F << 4) | (E & 0xF))) //
.putShort((short) proto.index) //
;
}
}
// AA|op BBBB CCCC HHHH
public static class OP4rcc extends OpInsn {
final BaseItem mtd;
final BaseItem proto;
final int length;
final int start;
public OP4rcc(Op op, int[] args, BaseItem mtd, BaseItem proto) {
super(op);
this.mtd = mtd;
this.proto = proto;
length = args.length;
checkContentUByte(op, "AA", length);
if (length > 0) {
start = args[0];
checkContentUShort(op, "CCCC", start);
for (int i = 1; i < args.length; i++) {
if (start + i != args[i]) {
throw new CantNotFixContentException(op, "a", args[i]);
}
}
} else {
start = 0;
}
}
@Override
public void write(ByteBuffer out) {
checkContentUShort(op, "@BBBB", mtd.index);
checkContentUShort(op, "@HHHH", proto.index);
out
.put((byte) op.opcode).put((byte) length) //
.putShort((short) mtd.index) //
.putShort((short) start) //
.putShort((short) proto.index) //
;
}
}
// AA|op BBBB CCCC
public static class OP3rc extends OpInsn {
final BaseItem item;

View File

@ -16,6 +16,7 @@
*/
package com.googlecode.d2j.dex.writer;
import com.googlecode.d2j.dex.writer.ev.EncodedArray;
import com.googlecode.d2j.dex.writer.io.ByteBufferOut;
import com.googlecode.d2j.dex.writer.io.DataOut;
import com.googlecode.d2j.dex.writer.item.*;
@ -124,6 +125,7 @@ public class DexFileWriter extends DexFileVisitor {
mapItem = new MapListItem();
headItem = new HeadItem();
headItem.version = cp.dexVersion;
SectionItem<HeadItem> headSection = new SectionItem<>(SectionType.TYPE_HEADER_ITEM);
headSection.items.add(headItem);
SectionItem<MapListItem> mapSection = new SectionItem<MapListItem>(SectionType.TYPE_MAP_LIST);
@ -160,8 +162,10 @@ public class DexFileWriter extends DexFileVisitor {
SectionType.TYPE_DEBUG_INFO_ITEM, cp.debugInfoItems);
SectionItem<AnnotationItem> annotationItemSection = new SectionItem<>(
SectionType.TYPE_ANNOTATION_ITEM, cp.annotationItems.values());
SectionItem<EncodedArrayItem> encodedArrayItemSection = new SectionItem<>(
SectionType.TYPE_ENCODED_ARRAY_ITEM, cp.encodedArrayItems);
SectionItem<EncodedArray> encodedArrayItemSection = new SectionItem<>(
SectionType.TYPE_ENCODED_ARRAY_ITEM, cp.encodedArrayItems.values());
SectionItem<CallSiteIdItem> callSiteIdItemSectionItem = new SectionItem<>(
SectionType.TYPE_CALL_SITE_ID_ITEM, cp.callSiteIdItems.values());
SectionItem<AnnotationsDirectoryItem> annotationsDirectoryItemSection = new SectionItem<>(
SectionType.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
cp.annotationsDirectoryItems);
@ -202,8 +206,13 @@ public class DexFileWriter extends DexFileVisitor {
items.add(protoIdSection);
items.add(fieldIdSection);
items.add(methodIdSection);
items.add(methodHandlerSection);
items.add(classDefSection);
if (callSiteIdItemSectionItem.items.size() > 0) {
items.add(callSiteIdItemSectionItem);
}
if (methodHandlerSection.items.size() > 0) {
items.add(methodHandlerSection);
}
items.addAll(dataSectionItems);
}

View File

@ -25,7 +25,7 @@ import com.googlecode.d2j.dex.writer.item.TypeIdItem;
import java.util.ArrayList;
import java.util.List;
public class EncodedAnnotation {
public class EncodedAnnotation implements Comparable<EncodedAnnotation> {
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -46,7 +46,33 @@ public class EncodedAnnotation {
return result;
}
public static class AnnotationElement {
@Override
public int compareTo(EncodedAnnotation o) {
if (o == null) {
return 1;
}
int x = type.compareTo(o.type);
if (x != 0) {
return x;
}
x = Integer.compare(elements.size(), o.elements.size());
if (x != 0) {
return x;
}
for (int i = 0; i < elements.size(); i++) {
AnnotationElement a = elements.get(i);
AnnotationElement b = o.elements.get(i);
x = a.compareTo(b);
if (x != 0) {
return x;
}
}
return 0;
}
public static class AnnotationElement implements Comparable<AnnotationElement> {
public StringIdItem name;
public EncodedValue value;
@ -69,6 +95,18 @@ public class EncodedAnnotation {
result = 31 * result + value.hashCode();
return result;
}
@Override
public int compareTo(AnnotationElement o) {
if (o == null) {
return 1;
}
int x = name.compareTo(o.name);
if (x != 0) {
return x;
}
return value.compareTo(o.value);
}
}
@Idx

View File

@ -20,9 +20,10 @@ import com.googlecode.d2j.dex.writer.io.DataOut;
import com.googlecode.d2j.dex.writer.item.BaseItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EncodedArray {
public class EncodedArray extends BaseItem implements Comparable<EncodedArray> {
public List<EncodedValue> values = new ArrayList<>(5);
@ -57,4 +58,24 @@ public class EncodedArray {
ev.write(out);
}
}
@Override
public int compareTo(EncodedArray o) {
if (o == null) {
return 1;
}
int x = Integer.compare(values.size(), o.values.size());
if (x != 0) {
return x;
}
for (int i = 0; i < values.size(); i++) {
EncodedValue a = values.get(i);
EncodedValue b = o.values.get(i);
x = a.compareTo(b);
if (x != 0) {
return x;
}
}
return 0;
}
}

View File

@ -17,9 +17,15 @@
package com.googlecode.d2j.dex.writer.ev;
import com.googlecode.d2j.dex.writer.io.DataOut;
import com.googlecode.d2j.dex.writer.item.*;
import com.googlecode.d2j.dex.writer.item.BaseItem;
import com.googlecode.d2j.dex.writer.item.FieldIdItem;
import com.googlecode.d2j.dex.writer.item.MethodHandleItem;
import com.googlecode.d2j.dex.writer.item.MethodIdItem;
import com.googlecode.d2j.dex.writer.item.ProtoIdItem;
import com.googlecode.d2j.dex.writer.item.StringIdItem;
import com.googlecode.d2j.dex.writer.item.TypeIdItem;
public class EncodedValue {
public class EncodedValue implements Comparable<EncodedValue> {
public final static int VALUE_ANNOTATION = 0x1d;
public final static int VALUE_ARRAY = 0x1c;
@ -32,6 +38,8 @@ public class EncodedValue {
public final static int VALUE_FLOAT = 0x10;
public final static int VALUE_INT = 0x04;
public final static int VALUE_LONG = 0x06;
public final static int VALUE_METHOD_TYPE = 0x15;
public final static int VALUE_METHOD_HANDLE = 0x16;
public final static int VALUE_METHOD = 0x1a;
public final static int VALUE_NULL = 0x1e;
public final static int VALUE_SHORT = 0x02;
@ -118,6 +126,10 @@ public class EncodedValue {
return new EncodedValue(VALUE_FIELD, v);
} else if (v instanceof MethodIdItem) {
return new EncodedValue(VALUE_METHOD, v);
} else if (v instanceof MethodHandleItem) {
return new EncodedValue(VALUE_METHOD_HANDLE, v);
} else if (v instanceof ProtoIdItem) {
return new EncodedValue(VALUE_METHOD_TYPE, v);
}
@ -258,6 +270,8 @@ public class EncodedValue {
case VALUE_FIELD:
case VALUE_METHOD:
case VALUE_ENUM:
case VALUE_METHOD_HANDLE:
case VALUE_METHOD_TYPE:
default:
return offset + getValueArg() + 1;
}
@ -289,6 +303,8 @@ public class EncodedValue {
case VALUE_FIELD:
case VALUE_METHOD:
case VALUE_ENUM:
case VALUE_METHOD_HANDLE:
case VALUE_METHOD_TYPE:
BaseItem bi = (BaseItem) value;
return lengthOfUint(bi.index) - 1;
}
@ -331,6 +347,8 @@ public class EncodedValue {
case VALUE_FIELD:
case VALUE_METHOD:
case VALUE_ENUM:
case VALUE_METHOD_HANDLE:
case VALUE_METHOD_TYPE:
out.bytes("value_xidx", encodeLong(valueArg + 1, ((BaseItem) value).index));
break;
case VALUE_ARRAY: {
@ -363,4 +381,55 @@ public class EncodedValue {
return s;
}
@Override
public int compareTo(EncodedValue o) {
if (o == null) {
return 1;
}
int x = Integer.compare(valueType, o.valueType);
if (x != 0) {
return x;
}
switch (valueType) {
case VALUE_NULL:
return 0;
case VALUE_BOOLEAN:
return Boolean.compare((Boolean) value, (Boolean) o.value);
case VALUE_SHORT:
return ((Short) value).compareTo((Short) o.value);
case VALUE_CHAR:
return ((Character) value).compareTo((Character) o.value);
case VALUE_INT:
return ((Integer) value).compareTo((Integer) o.value);
case VALUE_LONG:
return ((Long) value).compareTo((Long) o.value);
case VALUE_DOUBLE:
return Long.compare(Double.doubleToLongBits((Double) value), Double.doubleToLongBits((Double) o.value));
case VALUE_FLOAT:
return Integer.compare(Float.floatToIntBits((Float) value), Float.floatToIntBits((Float) o.value));
case VALUE_STRING:
case VALUE_TYPE:
case VALUE_FIELD:
case VALUE_METHOD:
case VALUE_ENUM:
case VALUE_METHOD_HANDLE:
case VALUE_METHOD_TYPE:
if (value instanceof Comparable) {
return ((Comparable) value).compareTo(value);
} else {
throw new RuntimeException();
}
case VALUE_ARRAY: {
return ((EncodedArray) value).compareTo((EncodedArray) o.value);
}
case VALUE_ANNOTATION: {
return ((EncodedAnnotation) value).compareTo((EncodedAnnotation) o.value);
}
case VALUE_BYTE: {
return Byte.compare((Byte) value, (Byte) o.value);
}
default:
throw new RuntimeException();
}
}
}

View File

@ -0,0 +1,46 @@
package com.googlecode.d2j.dex.writer.item;
import com.googlecode.d2j.dex.writer.ev.EncodedArray;
import com.googlecode.d2j.dex.writer.io.DataOut;
public class CallSiteIdItem extends BaseItem implements Comparable<CallSiteIdItem> {
String name;
EncodedArray encodedArrayItem;
public CallSiteIdItem(String name, EncodedArray encodedArrayItem) {
this.name = name;
this.encodedArrayItem = encodedArrayItem;
}
@Override
public void write(DataOut out) {
out.uint("call_site_off", encodedArrayItem.offset);
}
@Override
public int place(int offset) {
return offset + 4;
}
@Override
public int compareTo(CallSiteIdItem o) {
if (o == null) {
return 1;
}
if (name != null) {
if (o.name == null) {
return 1;
} else {
int x = name.compareTo(o.name);
if (x != 0) {
return x;
}
}
} else {
if (o.name != null) {
return -1;
}
}
return encodedArrayItem.compareTo(o.encodedArrayItem);
}
}

View File

@ -45,7 +45,7 @@ public class ClassDefItem extends BaseItem {
@Off
private AnnotationsDirectoryItem annotations;// Build later
@Off
private EncodedArrayItem staticValues; // Build later
private EncodedArray staticValues; // Build later
@Override
public int place(int offset) {
@ -77,8 +77,7 @@ public class ClassDefItem extends BaseItem {
}
if (count >= 0) {
EncodedArrayItem encodedArrayItem = cp.putEnCodedArrayItem();
EncodedArray array = encodedArrayItem.value;
EncodedArray array = new EncodedArray();
for (int i = 0; i <= count; i++) {
EncodedField f = fs.get(i);
EncodedValue ev = f.staticValue;
@ -88,7 +87,7 @@ public class ClassDefItem extends BaseItem {
array.values.add(ev);
}
}
staticValues = encodedArrayItem;
staticValues = cp.uniqEncodedArrayItem(array);
}
}

View File

@ -16,17 +16,24 @@
*/
package com.googlecode.d2j.dex.writer.item;
import com.googlecode.d2j.CallSite;
import com.googlecode.d2j.DexConstants;
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 com.googlecode.d2j.dex.writer.ev.EncodedArray;
import com.googlecode.d2j.dex.writer.ev.EncodedValue;
import java.util.*;
public class ConstPool {
public List<EncodedArrayItem> encodedArrayItems = new ArrayList<>();
public int dexVersion = DexConstants.DEX_035;
public Map<CallSiteIdItem, CallSiteIdItem> callSiteIdItems = new TreeMap<>();
public Map<EncodedArray, EncodedArray> encodedArrayItems = new TreeMap<>();
public Map<AnnotationSetRefListItem, AnnotationSetRefListItem> annotationSetRefListItems = new HashMap<>();
public List<CodeItem> codeItems = new ArrayList<>();
public List<ClassDataItem> classDataItems = new ArrayList<>();
@ -46,17 +53,17 @@ public class ConstPool {
public Object wrapEncodedItem(Object value) {
if (value instanceof DexType) {
value = uniqType(((DexType) value).desc);
return uniqType(((DexType) value).desc);
} else if (value instanceof Field) {
value = uniqField((Field) value);
return uniqField((Field) value);
} else if (value instanceof String) {
value = uniqString((String) value);
return uniqString((String) value);
} else if (value instanceof Method) {
value = uniqMethod((Method) value);
return uniqMethod((Method) value);
} else if (value instanceof MethodHandle) {
value = uniqMethodHandle((MethodHandle) value);
return uniqMethodHandle((MethodHandle) value);
} else if (value instanceof Proto) {
value = uniqProto((Proto) value);
return uniqProto((Proto) value);
}
return value;
}
@ -129,6 +136,18 @@ public class ConstPool {
debugInfoItems.add(debugInfoItem);
}
public void dex039() {
if (dexVersion < DexConstants.DEX_039) {
dexVersion = DexConstants.DEX_039;
}
}
public void dex038() {
if (dexVersion < DexConstants.DEX_038) {
dexVersion = DexConstants.DEX_038;
}
}
static class PE {
final ClassDefItem owner;
final Iterator<TypeIdItem> it;
@ -249,7 +268,7 @@ public class ConstPool {
return key;
}
private ProtoIdItem uniqProto(Proto method) {
public ProtoIdItem uniqProto(Proto method) {
return uniqProto(method.getParameterTypes(), method.getReturnType());
}
private ProtoIdItem uniqProto(Method method) {
@ -337,11 +356,31 @@ public class ConstPool {
return dataItem;
}
// TODO change EncodedArrayItem to uniq
public EncodedArrayItem putEnCodedArrayItem() {
EncodedArrayItem arrayItem = new EncodedArrayItem();
encodedArrayItems.add(arrayItem);
return arrayItem;
public CallSiteIdItem uniqCallSite(CallSite callSite) {
EncodedArray e = new EncodedArray();
e.values.add(new EncodedValue(EncodedValue.VALUE_METHOD_HANDLE, uniqMethodHandle(callSite.getBootstrapMethodHandler())));
e.values.add(new EncodedValue(EncodedValue.VALUE_STRING, uniqString(callSite.getMethodName())));
e.values.add(new EncodedValue(EncodedValue.VALUE_METHOD_TYPE, uniqProto(callSite.getMethodProto())));
for (Object arg : callSite.getExtraArguments()) {
e.values.add(EncodedValue.wrap(wrapEncodedItem(arg)));
}
CallSiteIdItem k = new CallSiteIdItem(callSite.getName(), uniqEncodedArrayItem(e));
CallSiteIdItem v = callSiteIdItems.get(k);
if (v == null) {
v = k;
callSiteIdItems.put(v, v);
}
return v;
}
public EncodedArray uniqEncodedArrayItem(EncodedArray k) {
EncodedArray v = encodedArrayItems.get(k);
if (v == null) {
v = k;
encodedArrayItems.put(v, v);
}
return v;
}
public AnnotationSetItem uniqAnnotationSetItem(AnnotationSetItem key) {

View File

@ -1,34 +0,0 @@
/*
* dex2jar - Tools to work with android .dex and java .class files
* Copyright (c) 2009-2013 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.dex.writer.item;
import com.googlecode.d2j.dex.writer.ev.EncodedArray;
import com.googlecode.d2j.dex.writer.io.DataOut;
public class EncodedArrayItem extends BaseItem {
public EncodedArray value = new EncodedArray();
@Override
public int place(int offset) {
return value.place(offset);
}
@Override
public void write(DataOut out) {
value.write(out);
}
}

View File

@ -16,13 +16,14 @@
*/
package com.googlecode.d2j.dex.writer.item;
import com.googlecode.d2j.DexConstants;
import com.googlecode.d2j.dex.writer.io.DataOut;
public class HeadItem extends BaseItem {
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public static final int V035 = 0x00353330;
public static final int V036 = 0x00363330;
public int version = V035;
public class HeadItem extends BaseItem {
public int version = DexConstants.DEX_035;
public SectionItem<MapListItem> mapSection;
public SectionItem<StringIdItem> stringIdSection;
public SectionItem<TypeIdItem> typeIdSection;
@ -34,7 +35,9 @@ public class HeadItem extends BaseItem {
public void write(DataOut out) {
out.uint("magic", 0x0A786564);
out.uint("version", version);
// version in DexConstants is big endian
out.bytes("version", writeBigEndian(version << 8));
out.skip4("checksum");
out.skip("signature", 20);
out.uint("file_size", fileSize);
@ -64,6 +67,11 @@ public class HeadItem extends BaseItem {
}
private static byte[] writeBigEndian(int value) {
return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN)
.putInt(value).array();
}
@Override
public int place(int offset) {
return offset + 0x70;

View File

@ -28,7 +28,6 @@ import java.util.List;
public class SectionItem<T extends BaseItem> extends BaseItem {
final public SectionType sectionType;
public final List<T> items = new ArrayList<>();
int size = 0;
public SectionItem(SectionType typeCode) {
super();
@ -52,10 +51,6 @@ public class SectionItem<T extends BaseItem> extends BaseItem {
}
}
public int size() {
return size;
}
public int place(int offset) {
final int startOffset = offset;
int index = 0;
@ -66,7 +61,6 @@ public class SectionItem<T extends BaseItem> extends BaseItem {
index++;
offset = t.place(offset);
}
size = offset - startOffset;
return offset;
}