summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/cf/direct/CodeObserver.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/cf/direct/CodeObserver.java')
-rw-r--r--dx/src/com/android/dx/cf/direct/CodeObserver.java306
1 files changed, 306 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/cf/direct/CodeObserver.java b/dx/src/com/android/dx/cf/direct/CodeObserver.java
new file mode 100644
index 0000000..efcc80b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/CodeObserver.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2007 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.cf.direct;
+
+import com.android.dx.cf.code.ByteOps;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.SwitchList;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Bytecode visitor to use when "observing" bytecode getting parsed.
+ */
+public class CodeObserver implements BytecodeArray.Visitor {
+ /** {@code non-null;} actual array of bytecode */
+ private final ByteArray bytes;
+
+ /** {@code non-null;} observer to inform of parsing */
+ private final ParseObserver observer;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} actual array of bytecode
+ * @param observer {@code non-null;} observer to inform of parsing
+ */
+ public CodeObserver(ByteArray bytes, ParseObserver observer) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (observer == null) {
+ throw new NullPointerException("observer == null");
+ }
+
+ this.bytes = bytes;
+ this.observer = observer;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ observer.parsed(bytes, offset, length, header(offset));
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length, Type type) {
+ observer.parsed(bytes, offset, length, header(offset));
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
+ boolean argComment = (length == 1);
+ String valueStr = "";
+
+ if (opcode == ByteOps.IINC) {
+ valueStr = ", #" +
+ ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
+ }
+
+ String catStr = "";
+ if (type.isCategory2()) {
+ catStr = (argComment ? "," : " //") + " category-2";
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + (argComment ? " // " : " ") +
+ idxStr + valueStr + catStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ if (cst instanceof CstKnownNull) {
+ // This is aconst_null.
+ visitNoArgs(opcode, offset, length, null);
+ return;
+ }
+
+ if (cst instanceof CstInteger) {
+ visitLiteralInt(opcode, offset, length, value);
+ return;
+ }
+
+ if (cst instanceof CstLong) {
+ visitLiteralLong(opcode, offset, length,
+ ((CstLong) cst).getValue());
+ return;
+ }
+
+ if (cst instanceof CstFloat) {
+ visitLiteralFloat(opcode, offset, length,
+ ((CstFloat) cst).getIntBits());
+ return;
+ }
+
+ if (cst instanceof CstDouble) {
+ visitLiteralDouble(opcode, offset, length,
+ ((CstDouble) cst).getLongBits());
+ return;
+ }
+
+ String valueStr = "";
+ if (value != 0) {
+ valueStr = ", ";
+ if (opcode == ByteOps.MULTIANEWARRAY) {
+ valueStr += Hex.u1(value);
+ } else {
+ valueStr += Hex.u2(value);
+ }
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + " " + cst + valueStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
+ observer.parsed(bytes, offset, length,
+ header(offset) + " " + targetStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ int sz = cases.size();
+ StringBuffer sb = new StringBuffer(sz * 20 + 100);
+
+ sb.append(header(offset));
+ if (padding != 0) {
+ sb.append(" // padding: " + Hex.u4(padding));
+ }
+ sb.append('\n');
+
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(Hex.s4(cases.getValue(i)));
+ sb.append(": ");
+ sb.append(Hex.u2(cases.getTarget(i)));
+ sb.append('\n');
+ }
+
+ sb.append(" default: ");
+ sb.append(Hex.u2(cases.getDefaultTarget()));
+
+ observer.parsed(bytes, offset, length, sb.toString());
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType cst,
+ ArrayList<Constant> intVals) {
+ String commentOrSpace = (length == 1) ? " // " : " ";
+ String typeName = cst.getClassType().getComponentType().toHuman();
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrSpace + typeName);
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviousOffset(int offset) {
+ // Do nothing
+ }
+
+ /** {@inheritDoc} */
+ public int getPreviousOffset() {
+ return -1;
+ }
+
+ /**
+ * Helper to produce the first bit of output for each instruction.
+ *
+ * @param offset the offset to the start of the instruction
+ */
+ private String header(int offset) {
+ /*
+ * Note: This uses the original bytecode, not the
+ * possibly-transformed one.
+ */
+ int opcode = bytes.getUnsignedByte(offset);
+ String name = ByteOps.opName(opcode);
+
+ if (opcode == ByteOps.WIDE) {
+ opcode = bytes.getUnsignedByte(offset + 1);
+ name += " " + ByteOps.opName(opcode);
+ }
+
+ return Hex.u2(offset) + ": " + name;
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is an
+ * {@code int}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param value constant value
+ */
+ private void visitLiteralInt(int opcode, int offset, int length,
+ int value) {
+ String commentOrSpace = (length == 1) ? " // " : " ";
+ String valueStr;
+
+ opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
+ if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
+ valueStr = "#" + Hex.s1(value);
+ } else if (opcode == ByteOps.SIPUSH) {
+ valueStr = "#" + Hex.s2(value);
+ } else {
+ valueStr = "#" + Hex.s4(value);
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrSpace + valueStr);
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code long}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param value constant value
+ */
+ private void visitLiteralLong(int opcode, int offset, int length,
+ long value) {
+ String commentOrLit = (length == 1) ? " // " : " #";
+ String valueStr;
+
+ if (length == 1) {
+ valueStr = Hex.s1((int) value);
+ } else {
+ valueStr = Hex.s8(value);
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrLit + valueStr);
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code float}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param bits constant value, as float-bits
+ */
+ private void visitLiteralFloat(int opcode, int offset, int length,
+ int bits) {
+ String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + optArg + " // " +
+ Float.intBitsToFloat(bits));
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code double}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param bits constant value, as double-bits
+ */
+ private void visitLiteralDouble(int opcode, int offset, int length,
+ long bits) {
+ String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + optArg + " // " +
+ Double.longBitsToDouble(bits));
+ }
+}