blob: 1d453d913da407511752bade236af58372a72b69 [file] [log] [blame]
/*
* 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.jack.dx.dex.code;
import com.android.jack.dx.io.Opcodes;
import com.android.jack.dx.rop.cst.Constant;
import com.android.jack.dx.rop.cst.CstBaseMethodRef;
import com.android.jack.dx.util.AnnotatedOutput;
import com.android.jack.dx.util.ExceptionWithContext;
import com.android.jack.dx.util.FixedSizeList;
import com.android.jack.dx.util.IndentingWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
/**
* List of {@link DalvInsn} instances.
*/
public final class DalvInsnList extends FixedSizeList {
/**
* The amount of register space, in register units, required for this
* code block. This may be greater than the largest observed register+
* category because the method this code block exists in may
* specify arguments that are unused by the method.
*/
private final int regCount;
/**
* Constructs and returns an immutable instance whose elements are
* identical to the ones in the given list, in the same order.
*
* @param list {@code non-null;} the list to use for elements
* @param regCount count, in register-units, of the number of registers
* this code block requires.
* @return {@code non-null;} an appropriately-constructed instance of this
* class
*/
public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list, int regCount) {
int size = list.size();
DalvInsnList result = new DalvInsnList(size, regCount);
for (int i = 0; i < size; i++) {
result.set(i, list.get(i));
}
result.setImmutable();
return result;
}
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size the size of the list
*/
public DalvInsnList(int size, int regCount) {
super(size);
this.regCount = regCount;
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public DalvInsn get(int n) {
return (DalvInsn) get0(n);
}
/**
* Sets the instruction at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param insn {@code non-null;} the instruction to set at {@code n}
*/
public void set(int n, DalvInsn insn) {
set0(n, insn);
}
/**
* Gets the size of this instance, in 16-bit code units. This will only
* return a meaningful result if the instructions in this instance all
* have valid addresses.
*
* @return {@code >= 0;} the size
*/
public int codeSize() {
int sz = size();
if (sz == 0) {
return 0;
}
DalvInsn last = get(sz - 1);
return last.getNextAddress();
}
/**
* Writes all the instructions in this instance to the given output
* destination.
*
* @param out {@code non-null;} where to write to
*/
public void writeTo(AnnotatedOutput out) {
int startCursor = out.getCursor();
int sz = size();
if (out.annotates()) {
boolean verbose = out.isVerbose();
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
int codeBytes = insn.codeSize() * 2;
String s;
if ((codeBytes != 0) || verbose) {
s = insn.listingString(" ", out.getAnnotationWidth(), true);
} else {
s = null;
}
if (s != null) {
out.annotate(codeBytes, s);
} else if (codeBytes != 0) {
out.annotate(codeBytes, "");
}
}
}
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
try {
insn.writeTo(out);
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex, "...while writing " + insn);
}
}
// Sanity check of the amount written.
int written = (out.getCursor() - startCursor) / 2;
if (written != codeSize()) {
throw new RuntimeException(
"write length mismatch; expected " + codeSize() + " but actually wrote " + written);
}
}
/**
* Gets the minimum required register count implied by this
* instance. This includes any unused parameters that could
* potentially be at the top of the register space.
* @return {@code >= 0;} the required registers size
*/
public int getRegistersSize() {
return regCount;
}
/**
* Gets the size of the outgoing arguments area required by this
* method. This is equal to the largest argument word count of any
* method referred to by this instance.
*
* @return {@code >= 0;} the required outgoing arguments size
*/
public int getOutsSize() {
int sz = size();
int result = 0;
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
if (!(insn instanceof CstInsn)) {
continue;
}
Constant cst = ((CstInsn) insn).getConstant();
if (!(cst instanceof CstBaseMethodRef)) {
continue;
}
boolean isStatic = (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC);
int count = ((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
if (count > result) {
result = count;
}
}
return result;
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
* @param verbose whether to be verbose; verbose output includes
* lines for zero-size instructions and explicit constant pool indices
*/
public void debugPrint(Writer out, String prefix, boolean verbose) {
@SuppressWarnings("resource")
IndentingWriter iw = new IndentingWriter(out, 0, prefix);
int sz = size();
try {
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
String s;
if ((insn.codeSize() != 0) || verbose) {
s = insn.listingString("", 0, verbose);
} else {
s = null;
}
if (s != null) {
iw.write(s);
}
}
iw.flush();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
* @param verbose whether to be verbose; verbose output includes
* lines for zero-size instructions
*/
public void debugPrint(OutputStream out, String prefix, boolean verbose) {
Writer w = new OutputStreamWriter(out);
debugPrint(w, prefix, verbose);
try {
w.flush();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}