
net.oneandone.mork.classfile.Code Maven / Gradle / Ivy
The newest version!
/*
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* 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 net.oneandone.mork.classfile;
import net.oneandone.mork.classfile.attribute.Attribute;
import net.oneandone.sushi.util.IntArrayList;
import net.oneandone.sushi.util.IntBitSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
public class Code extends Attribute implements Constants {
public static final Logger LOGGER = Logger.getLogger(Code.class.getName());
public int locals;
public List instructions;
public List exceptions;
/** List of Attributes */
public List attributes;
/** only valid while reading or writing. */
private int codeSize;
/**
* Labels to have forware references:
* Index i stores label -i; index 0 is not used
* -1: declared, but not defined
* otherwise: index
*/
private IntArrayList labels;
public Code() {
super("Code");
locals = 0;
instructions = new ArrayList();
exceptions = new ArrayList();
attributes = new ArrayList();
labels = new IntArrayList();
labels.add(0); // dummy
}
public Code(Input src) throws IOException {
this();
Instruction instr;
int i;
int max;
int loadedStack;
int computedStack;
src.readU4(); // attribute length is not used
loadedStack = src.readU2();
locals = src.readU2();
max = src.readU4();
src.openCode(this);
while (src.getOfs() < max) {
instr = Instruction.read(src);
instructions.add(instr);
}
codeSize = src.getOfs();
max = instructions.size();
for (i = 0; i < max; i++) {
instr = instructions.get(i);
instr.ofsToIdx(this);
}
max = src.readU2();
for (i = 0; i < max; i++) {
exceptions.add(new ExceptionInfo(src));
}
max = src.readU2();
for (i = 0; i < max; i++) {
attributes.add(Attribute.create(src));
}
src.closeCode();
computedStack = calcStackSize();
if (loadedStack < computedStack) {
LOGGER.warning(
"loaded stack size differs from computed: " +
"loaded: " + loadedStack + " < computed: " + computedStack);
}
}
public void references(Collection result) {
for (Object i : instructions) {
for (Object arg : ((Instruction) i).arguments) {
if (arg instanceof Reference) {
result.add((Reference) arg);
}
}
}
}
/**
* @return number of instructions.
*/
public int getSize() {
return instructions.size();
}
@Override
public void write(Output dest) throws IOException {
int i, max;
Instruction instr;
ExceptionInfo info;
Attribute attr;
int start;
layout(dest);
if (codeSize > 0xffff) {
throw new IllegalStateException("code segment exceeds 64k");
}
dest.writeUtf8(name);
start = dest.writeSpace(4);
dest.writeU2(calcStackSize());
dest.writeU2(locals);
dest.writeU4(codeSize);
dest.openCode(this);
max = instructions.size();
for (i = 0; i < max; i++) {
instr = (Instruction) instructions.get(i);
instr.write(dest);
}
max = exceptions.size();
dest.writeU2(max);
for (i = 0; i < max; i++) {
info = (ExceptionInfo) exceptions.get(i);
info.write(dest);
}
max = attributes.size();
dest.writeU2(max);
for (i = 0; i < max; i++) {
attr = (Attribute) attributes.get(i);
attr.write(dest);
}
dest.closeCode();
dest.writeFixup(start, dest.getGlobalOfs() - (start + 4));
}
/** compute ofs for all instructions. */
private void layout(Output dest) {
Instruction instr;
int instrSize, varSize;
IntArrayList vars; // indexes of instructions with variable length
IntArrayList lens; // lengths of var-len instructions
int i, j, k, ofs;
int shrink, len;
boolean changes;
instrSize = instructions.size();
vars = new IntArrayList();
lens = new IntArrayList();
ofs = 0;
for (i = 0; i < instrSize; i++) {
instr = instructions.get(i);
instr.ofs = ofs;
len = instr.getMaxLength(dest);
ofs += len;
if (instr.type.isVariable()) {
vars.add(i);
lens.add(len);
}
}
codeSize = ofs;
varSize = vars.size();
do {
changes = false;
shrink = 0;
for (i = 0; i < varSize; i++) {
j = vars.get(i);
instr = instructions.get(j);
len = instr.getVariableLength(this);
if (len < lens.get(i)) {
changes = true;
shrink += (lens.get(i) - len);
lens.set(i, len);
}
if (shrink > 0) {
// relocate up to end or next var-instr
for (k = (i + 1 == varSize)? instrSize - 1 : vars.get(i + 1); k > j; k--) {
instructions.get(k).ofs -= shrink;
}
}
}
codeSize -= shrink;
} while (changes);
}
//-- code context
public int findEndIdxOrLast(int startIdx, int len) {
return findIdxOrLast(instructions.get(startIdx).ofs + len);
}
public int findIdxOrLast(int ofs) {
if (ofs == codeSize) {
return instructions.size();
} else {
return findIdx(ofs);
}
}
public int findIdx(int ofs) {
// binary search
int low, high;
int idx;
int tmp;
low = 0;
high = instructions.size() - 1;
while (low <= high) {
idx = (low + high) / 2;
tmp = instructions.get(idx).ofs;
if (tmp < ofs) {
low = idx + 1;
} else if (tmp > ofs) {
high = idx - 1;
} else {
return idx;
}
}
throw new RuntimeException("no such ofs: " + ofs);
}
public int findEndOfsOrLast(int startIdx, int idx) {
return findOfsOrLast(idx) - findOfs(startIdx);
}
public int findOfsOrLast(int idx) {
if (idx == instructions.size()) {
return codeSize;
} else {
return findOfs(idx);
}
}
public int findOfs(int idx) {
return instructions.get(resolveLabel(idx)).ofs;
}
//--
// Labels are argument to goto and if..
// a forward reference is first declared and then defined
public int resolveLabel(int idx) {
int trueIdx;
if (idx < 0) {
trueIdx = labels.get(-idx);
if (trueIdx < 0) {
throw new RuntimeException("undefined label: " + idx);
}
return trueIdx;
} else {
return idx;
}
}
public int declareLabel() {
labels.add(-1);
return -(labels.size() - 1);
}
public void defineLabel(int label) {
if (labels.get(-label) != -1) {
throw new RuntimeException("duplicate definition for label " + label);
}
labels.set(-label, instructions.size());
}
public int currentLabel() {
return instructions.size();
}
//--
@Override
public String toString() {
StringBuilder result;
int i;
Instruction instr;
result = new StringBuilder();
result.append("Code attribute: \n");
result.append(" locals=" + locals);
result.append(" codeSize=" + codeSize + "\n");
for (i = 0; i < instructions.size(); i++) {
instr = instructions.get(i);
result.append(i + "\t" + "(" + instr.ofs + ")\t" +
instr.toString() + "\n");
}
for (i = 0; i < exceptions.size(); i++) {
result.append('\t');
result.append(exceptions.get(i).toString());
result.append('\n');
}
for (i = 0; i < attributes.size(); i++) {
result.append('\t');
result.append(attributes.get(i).toString());
result.append('\n');
}
return result.toString();
}
//--
/** pc is increased by 1. */
public void emitGeneric(int opcode, Object[] args) {
Instruction instr;
InstructionType type;
type = Set.TYPES[opcode];
instr = new Instruction(-1, type, args);
instructions.add(instr);
}
public void emit(int opcode) {
emitGeneric(opcode, new Object[] {});
}
public void emit(int opcode, int arg) {
emitGeneric(opcode, new Object[] { arg });
}
public void emit(int opcode, int arg0, int arg1) {
emitGeneric(opcode, new Object[] { arg0, arg1 });
}
public void emit(int opcode, String arg) {
emitGeneric(opcode, new Object[] { arg });
}
public void emit(int opcode, MethodRef arg) {
emitGeneric(opcode, new Object[] { arg });
}
public void emit(int opcode, ClassRef arg) {
emitGeneric(opcode, new Object[] { arg });
}
public void emit(int opcode, FieldRef arg) {
emitGeneric(opcode, new Object[] { arg });
}
public void emit(int opcode, int a, int b, int c, IntArrayList d) {
emitGeneric(opcode, new Object[] { a, b, c, d });
}
public void emit(int opcode, int a, IntArrayList b, IntArrayList c) {
emitGeneric(opcode, new Object[] { a, b, c });
}
//--
// similar to emit(), but with PC specified
public int declareFixup() {
int result;
result = instructions.size();
emit(Bytecodes.NOP);
return result;
}
public void fixupGeneric(int fixup, int opcode, Object[] args) {
Instruction instr;
InstructionType type;
type = Set.TYPES[opcode];
instr = new Instruction(-1, type, args);
instructions.set(fixup, instr);
}
public void fixup(int fixup, int opcode) {
fixupGeneric(fixup, opcode, new Object[] {});
}
public void fixup(int fixup, int opcode, int arg) {
fixupGeneric(fixup, opcode, new Object[] { arg });
}
public void fixup(int fixup, int opcode, int arg0, int arg1) {
fixupGeneric(fixup, opcode, new Object[] { arg0, arg1 });
}
public void fixup(int fixup, int opcode, String arg) {
fixupGeneric(fixup, opcode, new Object[] { arg });
}
public void fixup(int fixup, int opcode, MethodRef arg) {
fixupGeneric(fixup, opcode, new Object[] { arg });
}
public void fixup(int fixup, int opcode, ClassRef arg) {
fixupGeneric(fixup, opcode, new Object[] { arg });
}
public void fixup(int fixup, int opcode, FieldRef arg) {
fixupGeneric(fixup, opcode, new Object[] { arg });
}
public void fixup(int fixup, int opcode, int a, int b, int c, IntArrayList d) {
fixupGeneric(fixup, opcode, new Object[] { a, b, c, d });
}
public void fixup(int fixup, int opcode, int a, IntArrayList b, IntArrayList c) {
fixupGeneric(fixup, opcode, new Object[] { a, b, c });
}
//--
// computed the stack size
private int calcStackSize() {
int i, max;
int result;
int[] startStack;
ExceptionInfo e;
int tmp;
int unreachable;
IntBitSet todo;
List jsrs;
jsrs = Jsr.findJsrs(this);
startStack = new int[instructions.size()];
for (i = 0; i < startStack.length; i++) {
startStack[i] = -1;
}
startStack[0] = 0;
todo = new IntBitSet();
todo.add(0);
max = exceptions.size();
for (i = 0; i < max; i++) {
e = exceptions.get(i);
startStack[e.handler] = 1;
todo.add(e.handler);
}
fillStack(jsrs, todo, startStack);
result = 0;
unreachable = -1;
for (i = 0; i < startStack.length; i++) {
tmp = startStack[i];
if (tmp > result) {
result = tmp;
}
if ((unreachable == -1) && (tmp == -1)) {
unreachable = i;
}
}
if (unreachable != -1) {
// There are several class file in Sun JDK 1.3 with dead
// code, and they pass class file validation:
// (e.g. com.sun.corba.se.intneral.io.util.Arrays)
LOGGER.warning("unreachable code, starting " + unreachable + "\n");
}
return result;
}
private void fillStack(List jsrs, IntBitSet todo, int[] startStack) {
int succSize;
Instruction instr;
int idx;
int i, max;
int tmp, succIdx;
IntArrayList succBuffer;
while (true) {
idx = todo.first();
if (idx == -1) {
return;
}
todo.remove(idx);
instr = instructions.get(idx);
succSize = startStack[idx] + instr.getStackDiff();
if (succSize < 0) {
throw new RuntimeException("stack size <0, idx: " + idx);
}
succBuffer = new IntArrayList();
instr.getSuccessors(jsrs, idx, this, succBuffer);
max = succBuffer.size();
for (i = 0; i < max; i++) {
succIdx = succBuffer.get(i);
tmp = startStack[succIdx];
if (tmp == succSize) {
// do nothing, end of recursion
} else if (tmp == -1) {
startStack[succIdx] = succSize;
todo.add(succIdx);
} else {
throw new RuntimeException("wrong stack idx = " + succIdx
+ ": " + tmp + "!=" + succSize + "\n" + toString());
}
}
}
}
//-- TODO
public int allocate(ClassRef cl) {
int result;
result = locals;
locals += cl.operandSize();
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy