Add support invoke-polymorphic to dexdump
o Update bytecode.txt with codepoints for invoke-polymorphic
instructions and run opcode-gen/regen-all.
o Update dexdump to support invoke-polymorphic with output equivalent to
dexdump2.
o Add format classes to dexgen and dx.
Bug: 30550796
Test: manually tested with DEX files generated from ART run-tests.
Change-Id: I986a896747d73e11418ba1876cce86087b4a9e68
diff --git a/dexdump/DexDump.cpp b/dexdump/DexDump.cpp
index b186bbd..fd7542e 100644
--- a/dexdump/DexDump.cpp
+++ b/dexdump/DexDump.cpp
@@ -81,6 +81,13 @@
const char* signature;
};
+
+/* basic info about a prototype */
+struct ProtoInfo {
+ char* parameterTypes; // dynamically allocated with malloc
+ const char* returnType;
+};
+
/*
* Get 2 little-endian bytes.
*/
@@ -691,6 +698,63 @@
return true;
}
+/*
+ * Get information about a ProtoId.
+ */
+bool getProtoInfo(DexFile* pDexFile, u4 protoIdx, ProtoInfo* pProtoInfo)
+{
+ if (protoIdx >= pDexFile->pHeader->protoIdsSize) {
+ return false;
+ }
+
+ const DexProtoId* protoId = dexGetProtoId(pDexFile, protoIdx);
+
+ // Get string for return type.
+ if (protoId->returnTypeIdx >= pDexFile->pHeader->typeIdsSize) {
+ return false;
+ }
+ pProtoInfo->returnType = dexStringByTypeIdx(pDexFile, protoId->returnTypeIdx);
+
+ // Build string for parameter types.
+ size_t bufSize = 1;
+ char* buf = (char*)malloc(bufSize);
+ if (buf == NULL) {
+ return false;
+ }
+
+ buf[0] = '\0';
+ size_t bufUsed = 1;
+
+ const DexTypeList* paramTypes = dexGetProtoParameters(pDexFile, protoId);
+ if (paramTypes == NULL) {
+ // No parameters.
+ pProtoInfo->parameterTypes = buf;
+ return true;
+ }
+
+ for (u4 i = 0; i < paramTypes->size; ++i) {
+ if (paramTypes->list[i].typeIdx >= pDexFile->pHeader->typeIdsSize) {
+ free(buf);
+ return false;
+ }
+ const char* param = dexStringByTypeIdx(pDexFile, paramTypes->list[i].typeIdx);
+ size_t newUsed = bufUsed + strlen(param);
+ if (newUsed > bufSize) {
+ char* newBuf = (char*)realloc(buf, newUsed);
+ if (newBuf == NULL) {
+ free(buf);
+ return false;
+ }
+ buf = newBuf;
+ bufSize = newUsed;
+ }
+ strncat(buf + bufUsed - 1, param, bufSize - (bufUsed - 1));
+ bufUsed = newUsed;
+ }
+
+ pProtoInfo->parameterTypes = buf;
+ return true;
+}
/*
* Look up a class' descriptor.
@@ -718,6 +782,7 @@
int outSize;
u4 index;
+ u4 secondaryIndex = 0;
u4 width;
/* TODO: Make the index *always* be in field B, to simplify this code. */
@@ -742,6 +807,12 @@
index = pDecInsn->vC;
width = 4;
break;
+ case kFmt45cc:
+ case kFmt4rcc:
+ index = pDecInsn->vB; // method index
+ secondaryIndex = pDecInsn->arg[4]; // proto index
+ width = 4;
+ break;
default:
index = 0;
width = 4;
@@ -826,6 +897,24 @@
case kIndexFieldOffset:
outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
break;
+ case kIndexMethodAndProtoRef:
+ {
+ FieldMethodInfo methInfo;
+ ProtoInfo protoInfo;
+ protoInfo.parameterTypes = NULL;
+ if (getMethodInfo(pDexFile, index, &methInfo) &&
+ getProtoInfo(pDexFile, secondaryIndex, &protoInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s, (%s)%s // method@%0*x, proto@%0*x",
+ methInfo.classDescriptor, methInfo.name, methInfo.signature,
+ protoInfo.parameterTypes, protoInfo.returnType,
+ width, index, width, secondaryIndex);
+ } else {
+ outSize = snprintf(buf, bufSize, "<method?>, <proto?> // method@%0*x, proto@%0*x",
+ width, index, width, secondaryIndex);
+ }
+ free(protoInfo.parameterTypes);
+ }
+ break;
default:
outSize = snprintf(buf, bufSize, "<?>");
break;
@@ -1042,6 +1131,26 @@
break;
case kFmt00x: // unknown op or breakpoint
break;
+ case kFmt45cc:
+ {
+ fputs(" {", stdout);
+ printf("v%d", pDecInsn->vC);
+ for (int i = 0; i < (int) pDecInsn->vA - 1; ++i) {
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt4rcc:
+ {
+ fputs(" {", stdout);
+ printf("v%d", pDecInsn->vC);
+ for (int i = 1; i < (int) pDecInsn->vA; ++i) {
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
default:
printf(" ???");
break;
diff --git a/dexgen/src/com/android/dexgen/dex/code/DalvOps.java b/dexgen/src/com/android/dexgen/dex/code/DalvOps.java
index 1d051ea..732420f 100644
--- a/dexgen/src/com/android/dexgen/dex/code/DalvOps.java
+++ b/dexgen/src/com/android/dexgen/dex/code/DalvOps.java
@@ -281,8 +281,8 @@
public static final int UNUSED_F7 = 0xf7;
public static final int UNUSED_F8 = 0xf8;
public static final int UNUSED_F9 = 0xf9;
- public static final int UNUSED_FA = 0xfa;
- public static final int UNUSED_FB = 0xfb;
+ public static final int INVOKE_POLYMORPHIC = 0xfa;
+ public static final int INVOKE_POLYMORPHIC_RANGE = 0xfb;
public static final int UNUSED_FC = 0xfc;
public static final int UNUSED_FD = 0xfd;
public static final int UNUSED_FE = 0xfe;
diff --git a/dexgen/src/com/android/dexgen/dex/code/Dops.java b/dexgen/src/com/android/dexgen/dex/code/Dops.java
index afd21e3..0a2a6db 100644
--- a/dexgen/src/com/android/dexgen/dex/code/Dops.java
+++ b/dexgen/src/com/android/dexgen/dex/code/Dops.java
@@ -39,6 +39,8 @@
import com.android.dexgen.dex.code.form.Form32x;
import com.android.dexgen.dex.code.form.Form35c;
import com.android.dexgen.dex.code.form.Form3rc;
+import com.android.dexgen.dex.code.form.Form45cc;
+import com.android.dexgen.dex.code.form.Form4rcc;
import com.android.dexgen.dex.code.form.Form51l;
import com.android.dexgen.dex.code.form.SpecialFormat;
@@ -496,6 +498,10 @@
new Dop(DalvOps.INVOKE_INTERFACE, DalvOps.INVOKE_INTERFACE,
Form35c.THE_ONE, false, "invoke-interface");
+ public static final Dop INVOKE_POLYMORPHIC =
+ new Dop(DalvOps.INVOKE_POLYMORPHIC, DalvOps.INVOKE_POLYMORPHIC,
+ Form45cc.THE_ONE, false, "invoke-polymorphic");
+
public static final Dop INVOKE_VIRTUAL_RANGE =
new Dop(DalvOps.INVOKE_VIRTUAL_RANGE, DalvOps.INVOKE_VIRTUAL,
Form3rc.THE_ONE, false, "invoke-virtual/range");
@@ -516,6 +522,10 @@
new Dop(DalvOps.INVOKE_INTERFACE_RANGE, DalvOps.INVOKE_INTERFACE,
Form3rc.THE_ONE, false, "invoke-interface/range");
+ public static final Dop INVOKE_POLYMORPHIC_RANGE =
+ new Dop(DalvOps.INVOKE_POLYMORPHIC_RANGE, DalvOps.INVOKE_POLYMORPHIC,
+ Form4rcc.THE_ONE, false, "invoke-polymorphic/range");
+
public static final Dop NEG_INT =
new Dop(DalvOps.NEG_INT, DalvOps.NEG_INT,
Form12x.THE_ONE, true, "neg-int");
diff --git a/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java b/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java
new file mode 100644
index 0000000..9a37ba3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/form/Form45cc.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.android.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.rop.type.Type;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 45cc}. See the instruction format spec
+ * for details.
+ */
+public final class Form45cc extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form45cc();
+
+ /** Maximal number of operands */
+ private static final int MAX_NUM_OPS = 5;
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form45cc() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ return regListString(regs) + ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ return (wordCount(regs) >= 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form4rcc.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int cpi = ((CstInsn) insn).getIndex();
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ int sz = regs.size();
+ int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
+ int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
+ int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
+ int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
+ int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(r4, sz)), // encode the fifth operand here
+ (short) cpi,
+ codeUnit(r0, r1, r2, r3));
+ }
+
+ /**
+ * Gets the number of words required for the given register list, where
+ * category-2 values count as two words. Return {@code -1} if the
+ * list requires more than five words or contains registers that need
+ * more than a nibble to identify them.
+ *
+ * @param regs {@code non-null;} the register list in question
+ * @return {@code >= -1;} the number of words required, or {@code -1}
+ * if the list couldn't possibly fit in this format
+ */
+ private static int wordCount(RegisterSpecList regs) {
+ int sz = regs.size();
+
+ if (sz > MAX_NUM_OPS) {
+ // It can't possibly fit.
+ return -1;
+ }
+
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = regs.get(i);
+ result += one.getCategory();
+ /*
+ * The check below adds (category - 1) to the register, to
+ * account for the fact that the second half of a
+ * category-2 register has to be represented explicitly in
+ * the result.
+ */
+ if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
+ return -1;
+ }
+ }
+
+ return (result <= MAX_NUM_OPS) ? result : -1;
+ }
+
+ /**
+ * Returns a register list which is equivalent to the given one,
+ * except that it splits category-2 registers into two explicit
+ * entries. This returns the original list if no modification is
+ * required
+ *
+ * @param orig {@code non-null;} the original list
+ * @return {@code non-null;} the list with the described transformation
+ */
+ private static RegisterSpecList explicitize(RegisterSpecList orig) {
+ int wordCount = wordCount(orig);
+ int sz = orig.size();
+
+ if (wordCount == sz) {
+ return orig;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(wordCount);
+ int wordAt = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = orig.get(i);
+ result.set(wordAt, one);
+ if (one.getCategory() == 2) {
+ result.set(wordAt + 1,
+ RegisterSpec.make(one.getReg() + 1, Type.VOID));
+ wordAt += 2;
+ } else {
+ wordAt++;
+ }
+ }
+
+ result.setImmutable();
+ return result;
+ }
+}
diff --git a/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java b/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java
new file mode 100644
index 0000000..3c43de3
--- /dev/null
+++ b/dexgen/src/com/android/dexgen/dex/code/form/Form4rcc.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.android.dexgen.dex.code.form;
+
+import com.android.dexgen.dex.code.CstInsn;
+import com.android.dexgen.dex.code.DalvInsn;
+import com.android.dexgen.dex.code.InsnFormat;
+import com.android.dexgen.rop.code.RegisterSpec;
+import com.android.dexgen.rop.code.RegisterSpecList;
+import com.android.dexgen.rop.cst.Constant;
+import com.android.dexgen.rop.cst.CstMethodRef;
+import com.android.dexgen.rop.cst.CstType;
+import com.android.dexgen.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 4rcc}. See the instruction format spec
+ * for details.
+ */
+public final class Form4rcc extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form4rcc();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form4rcc() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int size = regs.size();
+ StringBuilder sb = new StringBuilder(30);
+
+ sb.append("{");
+
+ switch (size) {
+ case 0: {
+ // Nothing to do.
+ break;
+ }
+ case 1: {
+ sb.append(regs.get(0).regString());
+ break;
+ }
+ default: {
+ RegisterSpec lastReg = regs.get(size - 1);
+ if (lastReg.getCategory() == 2) {
+ /*
+ * Add one to properly represent a list-final
+ * category-2 register.
+ */
+ lastReg = lastReg.withOffset(1);
+ }
+
+ sb.append(regs.get(0).regString());
+ sb.append("..");
+ sb.append(lastReg.regString());
+ }
+ }
+
+ sb.append("}, ");
+ sb.append(cstString(insn));
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ int sz = regs.size();
+
+ if (sz == 0) {
+ return true;
+ }
+
+ int first = regs.get(0).getReg();
+ int next = first;
+
+ if (!unsignedFitsInShort(first)) {
+ return false;
+ }
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = regs.get(i);
+ if (one.getReg() != next) {
+ return false;
+ }
+ next += one.getCategory();
+ }
+
+ return unsignedFitsInByte(next - first);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int sz = regs.size();
+ int cpi = ((CstInsn) insn).getIndex();
+ int firstReg;
+ int count;
+
+ if (sz == 0) {
+ firstReg = 0;
+ count = 0;
+ } else {
+ int lastReg = regs.get(sz - 1).getNextReg();
+ firstReg = regs.get(0).getReg();
+ count = lastReg - firstReg;
+ }
+
+ write(out,
+ opcodeUnit(insn, count),
+ (short) cpi,
+ (short) firstReg);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java
index a84ddc0..f3c2b49 100644
--- a/dx/src/com/android/dx/dex/code/Dops.java
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -40,6 +40,8 @@
import com.android.dx.dex.code.form.Form32x;
import com.android.dx.dex.code.form.Form35c;
import com.android.dx.dex.code.form.Form3rc;
+import com.android.dx.dex.code.form.Form45cc;
+import com.android.dx.dex.code.form.Form4rcc;
import com.android.dx.dex.code.form.Form51l;
import com.android.dx.dex.code.form.SpecialFormat;
import com.android.dx.io.Opcodes;
@@ -499,6 +501,10 @@
new Dop(Opcodes.INVOKE_INTERFACE, Opcodes.INVOKE_INTERFACE,
Opcodes.INVOKE_INTERFACE_RANGE, Form35c.THE_ONE, false);
+ public static final Dop INVOKE_POLYMORPHIC =
+ new Dop(Opcodes.INVOKE_POLYMORPHIC, Opcodes.INVOKE_POLYMORPHIC,
+ Opcodes.INVOKE_POLYMORPHIC_RANGE, Form45cc.THE_ONE, false);
+
public static final Dop INVOKE_VIRTUAL_RANGE =
new Dop(Opcodes.INVOKE_VIRTUAL_RANGE, Opcodes.INVOKE_VIRTUAL,
Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
@@ -519,6 +525,10 @@
new Dop(Opcodes.INVOKE_INTERFACE_RANGE, Opcodes.INVOKE_INTERFACE,
Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
+ public static final Dop INVOKE_POLYMORPHIC_RANGE =
+ new Dop(Opcodes.INVOKE_POLYMORPHIC_RANGE, Opcodes.INVOKE_POLYMORPHIC,
+ Opcodes.NO_NEXT, Form4rcc.THE_ONE, false);
+
public static final Dop NEG_INT =
new Dop(Opcodes.NEG_INT, Opcodes.NEG_INT,
Opcodes.NO_NEXT, Form12x.THE_ONE, true);
@@ -1162,6 +1172,8 @@
set(SHL_INT_LIT8);
set(SHR_INT_LIT8);
set(USHR_INT_LIT8);
+ set(INVOKE_POLYMORPHIC);
+ set(INVOKE_POLYMORPHIC_RANGE);
// END(dops-init)
}
diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java
index fbfb0db..affa9eb 100644
--- a/dx/src/com/android/dx/dex/code/RopToDop.java
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -213,6 +213,7 @@
// Opcodes.SHL_INT_LIT8
// Opcodes.SHR_INT_LIT8
// Opcodes.USHR_INT_LIT8
+ // Opcodes.INVOKE_POLYMORPHIC
// END(first-opcodes)
static {
diff --git a/dx/src/com/android/dx/dex/code/form/Form45cc.java b/dx/src/com/android/dx/dex/code/form/Form45cc.java
new file mode 100644
index 0000000..4fe38f9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form45cc.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import java.util.BitSet;
+
+/**
+ * Instruction format {@code 45cc}. See the instruction format spec
+ * for details.
+ */
+public final class Form45cc extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form45cc();
+
+ /** Maximal number of operands */
+ private static final int MAX_NUM_OPS = 5;
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form45cc() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ return regListString(regs) + ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ return (wordCount(regs) >= 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BitSet compatibleRegs(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int sz = regs.size();
+ BitSet bits = new BitSet(sz);
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec reg = regs.get(i);
+ /*
+ * The check below adds (category - 1) to the register, to
+ * account for the fact that the second half of a
+ * category-2 register has to be represented explicitly in
+ * the result.
+ */
+ bits.set(i, unsignedFitsInNibble(reg.getReg() +
+ reg.getCategory() - 1));
+ }
+
+ return bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int cpi = ((CstInsn) insn).getIndex();
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ int sz = regs.size();
+ int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
+ int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
+ int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
+ int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
+ int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(r4, sz)), // encode the fifth operand here
+ (short) cpi,
+ codeUnit(r0, r1, r2, r3));
+ }
+
+ /**
+ * Gets the number of words required for the given register list, where
+ * category-2 values count as two words. Return {@code -1} if the
+ * list requires more than five words or contains registers that need
+ * more than a nibble to identify them.
+ *
+ * @param regs {@code non-null;} the register list in question
+ * @return {@code >= -1;} the number of words required, or {@code -1}
+ * if the list couldn't possibly fit in this format
+ */
+ private static int wordCount(RegisterSpecList regs) {
+ int sz = regs.size();
+
+ if (sz > MAX_NUM_OPS) {
+ // It can't possibly fit.
+ return -1;
+ }
+
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = regs.get(i);
+ result += one.getCategory();
+ /*
+ * The check below adds (category - 1) to the register, to
+ * account for the fact that the second half of a
+ * category-2 register has to be represented explicitly in
+ * the result.
+ */
+ if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
+ return -1;
+ }
+ }
+
+ return (result <= MAX_NUM_OPS) ? result : -1;
+ }
+
+ /**
+ * Returns a register list which is equivalent to the given one,
+ * except that it splits category-2 registers into two explicit
+ * entries. This returns the original list if no modification is
+ * required
+ *
+ * @param orig {@code non-null;} the original list
+ * @return {@code non-null;} the list with the described transformation
+ */
+ private static RegisterSpecList explicitize(RegisterSpecList orig) {
+ int wordCount = wordCount(orig);
+ int sz = orig.size();
+
+ if (wordCount == sz) {
+ return orig;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(wordCount);
+ int wordAt = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = orig.get(i);
+ result.set(wordAt, one);
+ if (one.getCategory() == 2) {
+ result.set(wordAt + 1,
+ RegisterSpec.make(one.getReg() + 1, Type.VOID));
+ wordAt += 2;
+ } else {
+ wordAt++;
+ }
+ }
+
+ result.setImmutable();
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form4rcc.java b/dx/src/com/android/dx/dex/code/form/Form4rcc.java
new file mode 100644
index 0000000..d4eca55
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form4rcc.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 4rcc}. See the instruction format spec
+ * for details.
+ */
+public final class Form4rcc extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form4rcc();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form4rcc() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ return regRangeString(insn.getRegisters()) + ", " +
+ cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 4;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+ Constant cst = ci.getConstant();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ int sz = regs.size();
+
+ return (regs.size() == 0) ||
+ (isRegListSequential(regs) &&
+ unsignedFitsInShort(regs.get(0).getReg()) &&
+ unsignedFitsInByte(regs.getWordCount()));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int cpi = ((CstInsn) insn).getIndex();
+ int firstReg = (regs.size() == 0) ? 0 : regs.get(0).getReg();
+ int count = regs.getWordCount();
+
+ write(out, opcodeUnit(insn, count), (short) cpi, (short) firstReg);
+ }
+}
diff --git a/dx/src/com/android/dx/io/IndexType.java b/dx/src/com/android/dx/io/IndexType.java
index bbddfa8..d873894 100644
--- a/dx/src/com/android/dx/io/IndexType.java
+++ b/dx/src/com/android/dx/io/IndexType.java
@@ -41,6 +41,9 @@
/** field reference index */
FIELD_REF,
+ /** method index and a proto index */
+ METHOD_AND_PROTO_REF,
+
/** inline method index (for inline linked method invocations) */
INLINE_METHOD,
diff --git a/dx/src/com/android/dx/io/OpcodeInfo.java b/dx/src/com/android/dx/io/OpcodeInfo.java
index 756d267..60225f1 100644
--- a/dx/src/com/android/dx/io/OpcodeInfo.java
+++ b/dx/src/com/android/dx/io/OpcodeInfo.java
@@ -931,6 +931,14 @@
new Info(Opcodes.USHR_INT_LIT8, "ushr-int/lit8",
InstructionCodec.FORMAT_22B, IndexType.NONE);
+ public static final Info INVOKE_POLYMORPHIC =
+ new Info(Opcodes.INVOKE_POLYMORPHIC, "invoke-polymorphic",
+ InstructionCodec.FORMAT_45CC, IndexType.METHOD_AND_PROTO_REF);
+
+ public static final Info INVOKE_POLYMORPHIC_RANGE =
+ new Info(Opcodes.INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range",
+ InstructionCodec.FORMAT_4RCC, IndexType.METHOD_AND_PROTO_REF);
+
// END(opcode-info-defs)
// Static initialization.
@@ -1164,6 +1172,8 @@
set(SHL_INT_LIT8);
set(SHR_INT_LIT8);
set(USHR_INT_LIT8);
+ set(INVOKE_POLYMORPHIC);
+ set(INVOKE_POLYMORPHIC_RANGE);
// END(opcode-info-init)
}
diff --git a/dx/src/com/android/dx/io/Opcodes.java b/dx/src/com/android/dx/io/Opcodes.java
index 611dbda..6f135de 100644
--- a/dx/src/com/android/dx/io/Opcodes.java
+++ b/dx/src/com/android/dx/io/Opcodes.java
@@ -259,6 +259,8 @@
public static final int SHL_INT_LIT8 = 0xe0;
public static final int SHR_INT_LIT8 = 0xe1;
public static final int USHR_INT_LIT8 = 0xe2;
+ public static final int INVOKE_POLYMORPHIC = 0xfa;
+ public static final int INVOKE_POLYMORPHIC_RANGE = 0xfb;
// END(opcodes)
// TODO: Generate these payload opcodes with opcode-gen.
diff --git a/dx/src/com/android/dx/io/instructions/InstructionCodec.java b/dx/src/com/android/dx/io/instructions/InstructionCodec.java
index 228352f..9c30575 100644
--- a/dx/src/com/android/dx/io/instructions/InstructionCodec.java
+++ b/dx/src/com/android/dx/io/instructions/InstructionCodec.java
@@ -604,6 +604,28 @@
}
},
+ FORMAT_45CC() {
+ @Override public DecodedInstruction decode(int opcodeUnit,
+ CodeInput in) throws EOFException {
+ return decodeRegisterList(this, opcodeUnit, in);
+ }
+
+ @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+ encodeRegisterList(insn, out);
+ }
+ },
+
+ FORMAT_4RCC() {
+ @Override public DecodedInstruction decode(int opcodeUnit,
+ CodeInput in) throws EOFException {
+ return decodeRegisterList(this, opcodeUnit, in);
+ }
+
+ @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+ encodeRegisterList(insn, out);
+ }
+ },
+
FORMAT_PACKED_SWITCH_PAYLOAD() {
@Override public DecodedInstruction decode(int opcodeUnit,
CodeInput in) throws EOFException {
diff --git a/libdex/DexOpcodes.cpp b/libdex/DexOpcodes.cpp
index bdcc558..d8f850e 100644
--- a/libdex/DexOpcodes.cpp
+++ b/libdex/DexOpcodes.cpp
@@ -280,8 +280,8 @@
"+iput-object-quick",
"+invoke-virtual-quick",
"+invoke-virtual-quick/range",
- "+invoke-super-quick",
- "+invoke-super-quick/range",
+ "invoke-polymorphic",
+ "invoke-polymorphic/range",
"+iput-object-volatile",
"+sget-object-volatile",
"+sput-object-volatile",
diff --git a/libdex/DexOpcodes.h b/libdex/DexOpcodes.h
index c2b9b80..09fdcdb 100644
--- a/libdex/DexOpcodes.h
+++ b/libdex/DexOpcodes.h
@@ -318,8 +318,8 @@
OP_IPUT_OBJECT_QUICK = 0xf7,
OP_INVOKE_VIRTUAL_QUICK = 0xf8,
OP_INVOKE_VIRTUAL_QUICK_RANGE = 0xf9,
- OP_INVOKE_SUPER_QUICK = 0xfa,
- OP_INVOKE_SUPER_QUICK_RANGE = 0xfb,
+ OP_INVOKE_POLYMORPHIC = 0xfa,
+ OP_INVOKE_POLYMORPHIC_RANGE = 0xfb,
OP_IPUT_OBJECT_VOLATILE = 0xfc,
OP_SGET_OBJECT_VOLATILE = 0xfd,
OP_SPUT_OBJECT_VOLATILE = 0xfe,
@@ -584,8 +584,8 @@
H(OP_IPUT_OBJECT_QUICK), \
H(OP_INVOKE_VIRTUAL_QUICK), \
H(OP_INVOKE_VIRTUAL_QUICK_RANGE), \
- H(OP_INVOKE_SUPER_QUICK), \
- H(OP_INVOKE_SUPER_QUICK_RANGE), \
+ H(OP_INVOKE_POLYMORPHIC), \
+ H(OP_INVOKE_POLYMORPHIC_RANGE), \
H(OP_IPUT_OBJECT_VOLATILE), \
H(OP_SGET_OBJECT_VOLATILE), \
H(OP_SPUT_OBJECT_VOLATILE), \
diff --git a/libdex/InstrUtils.cpp b/libdex/InstrUtils.cpp
index be343f0..3b7d552 100644
--- a/libdex/InstrUtils.cpp
+++ b/libdex/InstrUtils.cpp
@@ -47,7 +47,7 @@
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3,
- 3, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 0,
+ 3, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 2, 2, 2, 0,
// END(libdex-widths)
};
@@ -357,7 +357,7 @@
kFmt22b, kFmt22b, kFmt22b, kFmt22c, kFmt22c, kFmt21c, kFmt21c,
kFmt22c, kFmt22c, kFmt22c, kFmt21c, kFmt21c, kFmt00x, kFmt20bc,
kFmt35mi, kFmt3rmi, kFmt35c, kFmt10x, kFmt22cs, kFmt22cs, kFmt22cs,
- kFmt22cs, kFmt22cs, kFmt22cs, kFmt35ms, kFmt3rms, kFmt35ms, kFmt3rms,
+ kFmt22cs, kFmt22cs, kFmt22cs, kFmt35ms, kFmt3rms, kFmt45cc, kFmt4rcc,
kFmt22c, kFmt21c, kFmt21c, kFmt00x,
// END(libdex-formats)
};
@@ -451,7 +451,7 @@
kIndexMethodRef, kIndexNone, kIndexFieldOffset,
kIndexFieldOffset, kIndexFieldOffset, kIndexFieldOffset,
kIndexFieldOffset, kIndexFieldOffset, kIndexVtableOffset,
- kIndexVtableOffset, kIndexVtableOffset, kIndexVtableOffset,
+ kIndexVtableOffset, kIndexMethodAndProtoRef, kIndexMethodAndProtoRef,
kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
kIndexUnknown,
// END(libdex-index-types)
@@ -642,6 +642,29 @@
pDec->vA = INST_AA(inst);
pDec->vB_wide = FETCH_u4(1) | ((u8) FETCH_u4(3) << 32);
break;
+ case kFmt45cc:
+ {
+ // AG op BBBB FEDC HHHH
+ pDec->vA = INST_B(inst); // This is labelled A in the spec.
+ pDec->vB = FETCH(1); // vB meth@BBBB
+ u2 fedc = FETCH(2);
+ pDec->vC = fedc & 0xf;
+ pDec->arg[0] = (fedc >> 4) & 0xf; // vD
+ pDec->arg[1] = (fedc >> 8) & 0xf; // vE
+ pDec->arg[2] = (fedc >> 12); // vF
+ pDec->arg[3] = INST_A(inst); // vG
+ pDec->arg[4] = FETCH(3); // vH proto@HHHH
+ }
+ break;
+ case kFmt4rcc:
+ {
+ // AA op BBBB CCCC HHHH
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ pDec->vC = FETCH(2);
+ pDec->arg[4] = FETCH(3); // vH proto@HHHH
+ }
+ break;
default:
ALOGW("Can't decode unexpected format %d (op=%d)", format, opcode);
assert(false);
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
index 76993a5..2f34bd9 100644
--- a/libdex/InstrUtils.h
+++ b/libdex/InstrUtils.h
@@ -61,6 +61,8 @@
kFmt51l, // op vAA, #+BBBBBBBBBBBBBBBB
kFmt35mi, // [opt] inline invoke
kFmt3rmi, // [opt] inline invoke/range
+ kFmt45cc, // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH
+ kFmt4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH
};
/*
@@ -69,15 +71,16 @@
*/
enum InstructionIndexType {
kIndexUnknown = 0,
- kIndexNone, // has no index
- kIndexVaries, // "It depends." Used for throw-verification-error
- kIndexTypeRef, // type reference index
- kIndexStringRef, // string reference index
- kIndexMethodRef, // method reference index
- 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)
+ kIndexNone, // has no index
+ kIndexVaries, // "It depends." Used for throw-verification-error
+ kIndexTypeRef, // type reference index
+ kIndexStringRef, // string reference index
+ kIndexMethodRef, // method reference index
+ 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)
+ kIndexMethodAndProtoRef // method index and proto index
};
/*
diff --git a/opcode-gen/bytecode.txt b/opcode-gen/bytecode.txt
index 6b7b9b1..dc0778d 100644
--- a/opcode-gen/bytecode.txt
+++ b/opcode-gen/bytecode.txt
@@ -39,9 +39,10 @@
format 22c
format 22t
format 35c 3rc
+format 45cc 4rcc
# Optimized formats
-format 00x
+format 00x
format 20bc
format 22cs
format 35mi
@@ -67,6 +68,7 @@
# inline-method
# vtable-offset
# field-offset
+# method-and-proto-ref
# flags; pipe-combined combo of one or more of:
# optimized -- optimized; not to be included in unoptimized dex files
# branch -- might branch to an address
@@ -337,8 +339,13 @@
op f7 +iput-object-quick 22cs n field-offset optimized|continue|throw
op f8 +invoke-virtual-quick 35ms n vtable-offset optimized|continue|throw|invoke
op f9 +invoke-virtual-quick/range 3rms n vtable-offset optimized|continue|throw|invoke
-op fa +invoke-super-quick 35ms n vtable-offset optimized|continue|throw|invoke
-op fb +invoke-super-quick/range 3rms n vtable-offset optimized|continue|throw|invoke
+
+# Invoke-polymorphic
+op fa invoke-polymorphic 45cc y method-and-proto-ref continue|throw|invoke
+op fb invoke-polymorphic/range 4rcc y method-and-proto-ref continue|throw|invoke
+
+# More optimized opcodes (not valid in an unoptimized dex file)
+
op fc +iput-object-volatile 22c n field-ref optimized|continue|throw
op fd +sget-object-volatile 21c y field-ref optimized|continue|throw
op fe +sput-object-volatile 21c n field-ref optimized|continue|throw
diff --git a/opcode-gen/opcode-gen.awk b/opcode-gen/opcode-gen.awk
index e26a60c..d1e9a08 100644
--- a/opcode-gen/opcode-gen.awk
+++ b/opcode-gen/opcode-gen.awk
@@ -474,16 +474,17 @@
# Initialize the indexTypes data.
function initIndexTypes() {
- indexTypeValues["unknown"] = "kIndexUnknown";
- indexTypeValues["none"] = "kIndexNone";
- indexTypeValues["varies"] = "kIndexVaries";
- indexTypeValues["type-ref"] = "kIndexTypeRef";
- indexTypeValues["string-ref"] = "kIndexStringRef";
- indexTypeValues["method-ref"] = "kIndexMethodRef";
- indexTypeValues["field-ref"] = "kIndexFieldRef";
- indexTypeValues["inline-method"] = "kIndexInlineMethod";
- indexTypeValues["vtable-offset"] = "kIndexVtableOffset";
- indexTypeValues["field-offset"] = "kIndexFieldOffset";
+ indexTypeValues["unknown"] = "kIndexUnknown";
+ indexTypeValues["none"] = "kIndexNone";
+ indexTypeValues["varies"] = "kIndexVaries";
+ indexTypeValues["type-ref"] = "kIndexTypeRef";
+ indexTypeValues["string-ref"] = "kIndexStringRef";
+ indexTypeValues["method-ref"] = "kIndexMethodRef";
+ indexTypeValues["field-ref"] = "kIndexFieldRef";
+ indexTypeValues["inline-method"] = "kIndexInlineMethod";
+ indexTypeValues["vtable-offset"] = "kIndexVtableOffset";
+ indexTypeValues["field-offset"] = "kIndexFieldOffset";
+ indexTypeValues["method-and-proto-ref"] = "kIndexMethodAndProtoRef";
}
# Initialize the flags data.