diff options
Diffstat (limited to 'dx/src/com/android/dx/cf/direct/CodeObserver.java')
-rw-r--r-- | dx/src/com/android/dx/cf/direct/CodeObserver.java | 306 |
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)); + } +} |