/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.classfile; import java.io.*; import com.sun.cri.bytecode.*; import com.sun.cri.ci.*; import com.sun.cri.ri.*; import com.sun.max.annotate.*; import com.sun.max.program.*; import com.sun.max.vm.bytecode.*; import com.sun.max.vm.classfile.constant.*; /** * "Code" attributes in class files, see #4.7.3. */ public final class CodeAttribute { public static final ExceptionHandlerEntry[] NO_EXCEPTION_HANDLER_TABLE = ExceptionHandlerEntry.NONE; @INSPECTED public final ConstantPool cp; public final char maxStack; public final char maxLocals; @INSPECTED private final byte[] code; private StackMapTable stackMapTableAttribute; private final byte[] encodedData; private final int exceptionHandlerTableOffset; private final int lineNumberTableOffset; private final int localVariableTableOffset; private LineNumberTable lineNumberTable; public CodeAttribute(ConstantPool constantPool, byte[] code, char maxStack, char maxLocals, ExceptionHandlerEntry[] exceptionHandlerTable, LineNumberTable lineNumberTable, LocalVariableTable localVariableTable, StackMapTable stackMapTable) { this.cp = constantPool; this.code = code; this.maxStack = maxStack; this.maxLocals = maxLocals; this.stackMapTableAttribute = stackMapTable; final ByteArrayOutputStream encodingStream = new ByteArrayOutputStream(); final DataOutputStream dataOutputStream = new DataOutputStream(encodingStream); int exceptionHandlerTableOff = -1; int lineNumberTableOff = -1; int localVariableTableOff = -1; try { dataOutputStream.write(code); if (exceptionHandlerTable.length != 0) { exceptionHandlerTableOff = encodingStream.size(); ExceptionHandlerEntry.encode(exceptionHandlerTable, dataOutputStream); } if (!lineNumberTable.isEmpty()) { lineNumberTableOff = encodingStream.size(); lineNumberTable.encode(dataOutputStream); } if (!localVariableTable.isEmpty()) { localVariableTableOff = encodingStream.size(); localVariableTable.encode(dataOutputStream); } } catch (IOException e) { throw ProgramError.unexpected(e); } this.exceptionHandlerTableOffset = exceptionHandlerTableOff; this.lineNumberTableOffset = lineNumberTableOff; this.localVariableTableOffset = localVariableTableOff; encodedData = encodingStream.toByteArray(); } static void writeCharArray(DataOutputStream dataOutputStream, char[] buf) throws IOException { assert buf.length <= Short.MAX_VALUE; dataOutputStream.writeShort(buf.length); for (char c : buf) { dataOutputStream.writeChar(c); } } static char[] readCharArray(DataInputStream dataInputStream) throws IOException { final int length = dataInputStream.readUnsignedShort(); assert length != 0; final char[] buf = new char[length]; for (int i = 0; i != length; ++i) { buf[i] = dataInputStream.readChar(); } return buf; } public byte[] code() { return code; } public byte[] encodedData() { return encodedData; } private DataInputStream encodedData(int offset) { return new DataInputStream(new ByteArrayInputStream(encodedData, offset, encodedData.length - offset)); } /** * Gets the exception handler table as an array of triplets (start bci, end bci, handler bci). * * @return {@code null} if this code attribute has no exception handlers */ public int[] exceptionHandlerBCIs() { if (exceptionHandlerTableOffset == -1) { return null; } try { return ExceptionHandlerEntry.decodeHandlerBCIs(encodedData(exceptionHandlerTableOffset)); } catch (IOException e) { throw ProgramError.unexpected(e); } } public CiExceptionHandler[] exceptionHandlers() { ExceptionHandlerEntry[] exceptionHandlerTable = exceptionHandlerTable(); if (exceptionHandlerTable.length == 0) { return CiExceptionHandler.NONE; } CiExceptionHandler[] handlers = new CiExceptionHandler[exceptionHandlerTable.length]; int i = 0; for (ExceptionHandlerEntry entry : exceptionHandlerTable) { RiType catchType; int catchTypeIndex = entry.catchTypeIndex(); if (catchTypeIndex == 0) { catchType = null; } else { catchType = cp.classAt(catchTypeIndex).resolve(cp, catchTypeIndex); } handlers[i++] = new CiExceptionHandler( (char) entry.startBCI(), (char) entry.endBCI(), (char) entry.handlerBCI(), (char) catchTypeIndex, catchType); } return handlers; } public ExceptionHandlerEntry[] exceptionHandlerTable() { try { return exceptionHandlerTableOffset == -1 ? ExceptionHandlerEntry.NONE : ExceptionHandlerEntry.decode(encodedData(exceptionHandlerTableOffset)); } catch (IOException e) { throw ProgramError.unexpected(e); } } public LineNumberTable lineNumberTable() { if (lineNumberTable == null) { // cache the line number table try { lineNumberTable = lineNumberTableOffset == -1 ? LineNumberTable.EMPTY : LineNumberTable.decode(encodedData(lineNumberTableOffset)); } catch (IOException e) { throw ProgramError.unexpected(e); } } return lineNumberTable; } public LocalVariableTable localVariableTable() { try { return localVariableTableOffset == -1 ? LocalVariableTable.EMPTY : LocalVariableTable.decode(encodedData(localVariableTableOffset)); } catch (IOException e) { throw ProgramError.unexpected(e); } } /** * @return null if there is no stack map table associated with this code attribute */ public StackMapTable stackMapTable() { return stackMapTableAttribute; } public void setStackMapTableAttribute(StackMapTable stackMapTable) { stackMapTableAttribute = stackMapTable; } @Override public String toString() { try { return CodeAttributePrinter.toString(this); } catch (Exception e) { return super.toString() + "[" + e + "]"; } } /** * Gets the method called by an invoke instruction at a given BCI. * * @return the method invoked at offset {@code bci} in this code or {@code null} if * {@code bci} does not denote an invoke instruction */ public RiMethod calleeAt(int bci) { if (bci < 0 || bci >= code.length) { return null; } int opcode = code[bci] & 0xFF; if (Bytecodes.isInvoke(opcode)) { int cpi = com.sun.cri.bytecode.Bytes.beU2(code, bci + 1); MethodRefConstant callee = cp.methodAt(cpi); if (callee.isResolved()) { return callee.resolve(cp, cpi); } RiType holder = UnresolvedType.toRiType(callee.holder(cp), cp.holder()); RiSignature signature = callee.signature(cp); String name = callee.name(cp).string; return new UnresolvedMethod(cp, cpi, holder, name, signature); } return null; } }