/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.bytecode; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; /** * Interface to the bytecode parser. */ public class ByteCodeParser { private static final L10N L = new L10N(ByteCode.class); static final int CP_CLASS = 7; static final int CP_FIELD_REF = 9; static final int CP_METHOD_REF = 10; static final int CP_INTERFACE_METHOD_REF = 11; static final int CP_STRING = 8; static final int CP_INTEGER = 3; static final int CP_FLOAT = 4; static final int CP_LONG = 5; static final int CP_DOUBLE = 6; static final int CP_NAME_AND_TYPE = 12; static final int CP_UTF8 = 1; private JavaClassLoader _loader; private InputStream _is; private JavaClass _class; private ConstantPool _cp; /** * Sets the JClassLoader */ public void setClassLoader(JavaClassLoader loader) { _loader = loader; } /** * Sets the class. */ public void setJavaClass(JavaClass javaClass) { _class = javaClass; } /** * Parses the .class file. */ public JavaClass parse(InputStream is) throws IOException { _is = is; if (_loader == null) _loader = new JavaClassLoader(); if (_class == null) _class = new JavaClass(_loader); _cp = _class.getConstantPool(); parseClass(); return _class; } /** * Returns the constant pool. */ public ConstantPool getConstantPool() { return _cp; } /** * Returns a UTF8 String from the constant pool. */ public String getUTF8(int index) { return getConstantPool().getUtf8AsString(index); } /** * Parses the ClassFile construct */ private void parseClass() throws IOException { int magic = readInt(); if (magic != JavaClass.MAGIC) throw error(L.l("bad magic number in class file")); int minor = readShort(); int major = readShort(); _class.setMajor(major); _class.setMinor(minor); parseConstantPool(); int accessFlags = readShort(); _class.setAccessFlags(accessFlags); int thisClassIndex = readShort(); _class.setThisClass(_cp.getClass(thisClassIndex).getName()); int superClassIndex = readShort(); if (superClassIndex > 0) _class.setSuperClass(_cp.getClass(superClassIndex).getName()); int interfaceCount = readShort(); for (int i = 0; i < interfaceCount; i++) { int classIndex = readShort(); _class.addInterface(_cp.getClass(classIndex).getName()); } int fieldCount = readShort(); for (int i = 0; i < fieldCount; i++) { parseField(); } int methodCount = readShort(); for (int i = 0; i < methodCount; i++) parseMethod(); int attrCount = readShort(); for (int i = 0; i < attrCount; i++) { Attribute attr = parseAttribute(); _class.addAttribute(attr); } } /** * Parses the constant pool. */ public void parseConstantPool() throws IOException { int count = readShort(); for (int i = 1; i < count; i++) { ConstantPoolEntry entry = parseConstantPoolEntry(i); _cp.addConstant(entry); if (entry instanceof DoubleConstant || entry instanceof LongConstant) { i++; _cp.addConstant(null); } } } /** * Parses a constant pool entry. */ private ConstantPoolEntry parseConstantPoolEntry(int index) throws IOException { int tag = read(); switch (tag) { case CP_CLASS: return parseClassConstant(index); case CP_FIELD_REF: return parseFieldRefConstant(index); case CP_METHOD_REF: return parseMethodRefConstant(index); case CP_INTERFACE_METHOD_REF: return parseInterfaceMethodRefConstant(index); case CP_STRING: return parseStringConstant(index); case CP_INTEGER: return parseIntegerConstant(index); case CP_FLOAT: return parseFloatConstant(index); case CP_LONG: return parseLongConstant(index); case CP_DOUBLE: return parseDoubleConstant(index); case CP_NAME_AND_TYPE: return parseNameAndTypeConstant(index); case CP_UTF8: return parseUtf8Constant(index); default: throw error(L.l("'{0}' is an unknown constant pool type.", tag)); } } /** * Parses a class constant pool entry. */ private ClassConstant parseClassConstant(int index) throws IOException { int nameIndex = readShort(); return new ClassConstant(_class.getConstantPool(), index, nameIndex); } /** * Parses a field ref constant pool entry. */ private FieldRefConstant parseFieldRefConstant(int index) throws IOException { int classIndex = readShort(); int nameAndTypeIndex = readShort(); return new FieldRefConstant(_class.getConstantPool(), index, classIndex, nameAndTypeIndex); } /** * Parses a method ref constant pool entry. */ private MethodRefConstant parseMethodRefConstant(int index) throws IOException { int classIndex = readShort(); int nameAndTypeIndex = readShort(); return new MethodRefConstant(_class.getConstantPool(), index, classIndex, nameAndTypeIndex); } /** * Parses an interface method ref constant pool entry. */ private InterfaceMethodRefConstant parseInterfaceMethodRefConstant(int index) throws IOException { int classIndex = readShort(); int nameAndTypeIndex = readShort(); return new InterfaceMethodRefConstant(_class.getConstantPool(), index, classIndex, nameAndTypeIndex); } /** * Parses a string constant pool entry. */ private StringConstant parseStringConstant(int index) throws IOException { int stringIndex = readShort(); return new StringConstant(_class.getConstantPool(), index, stringIndex); } /** * Parses an integer constant pool entry. */ private IntegerConstant parseIntegerConstant(int index) throws IOException { int value = readInt(); return new IntegerConstant(_class.getConstantPool(), index, value); } /** * Parses a float constant pool entry. */ private FloatConstant parseFloatConstant(int index) throws IOException { int bits = readInt(); float value = Float.intBitsToFloat(bits); return new FloatConstant(_class.getConstantPool(), index, value); } /** * Parses a long constant pool entry. */ private LongConstant parseLongConstant(int index) throws IOException { long value = readLong(); return new LongConstant(_class.getConstantPool(), index, value); } /** * Parses a double constant pool entry. */ private DoubleConstant parseDoubleConstant(int index) throws IOException { long bits = readLong(); double value = Double.longBitsToDouble(bits); return new DoubleConstant(_class.getConstantPool(), index, value); } /** * Parses a name and type pool entry. */ private NameAndTypeConstant parseNameAndTypeConstant(int index) throws IOException { int nameIndex = readShort(); int descriptorIndex = readShort(); return new NameAndTypeConstant(_class.getConstantPool(), index, nameIndex, descriptorIndex); } /** * Parses a utf-8 constant pool entry. */ private Utf8Constant parseUtf8Constant(int index) throws IOException { int length = readShort(); CharBuffer cb = CharBuffer.allocate(); for (int i = 0; i < length; i++) { int ch = read(); if (ch < 0x80) { cb.append((char) ch); } else if ((ch & 0xe0) == 0xc0) { int ch2 = read(); i++; cb.append((char) (((ch & 0x1f) << 6)+ (ch2 & 0x3f))); } else { int ch2 = read(); int ch3 = read(); i += 2; cb.append((char) (((ch & 0xf) << 12)+ ((ch2 & 0x3f) << 6) + ((ch3 & 0x3f)))); } } return new Utf8Constant(_class.getConstantPool(), index, cb.close()); } /** * Parses a field entry. */ private void parseField() throws IOException { int accessFlags = readShort(); int nameIndex = readShort(); int descriptorIndex = readShort(); JavaField field = new JavaField(); field.setJavaClass(_class); field.setName(_cp.getUtf8(nameIndex).getValue()); field.setDescriptor(_cp.getUtf8(descriptorIndex).getValue()); field.setAccessFlags(accessFlags); int attributesCount = readShort(); for (int i = 0; i < attributesCount; i++) { Attribute attr = parseAttribute(); field.addAttribute(attr); } _class.addField(field); } /** * Parses a method entry. */ private void parseMethod() throws IOException { int accessFlags = readShort(); int nameIndex = readShort(); int descriptorIndex = readShort(); JavaMethod method = new JavaMethod(_loader); method.setJavaClass(_class); method.setName(_cp.getUtf8(nameIndex).getValue()); method.setDescriptor(_cp.getUtf8(descriptorIndex).getValue()); method.setAccessFlags(accessFlags); int attributesCount = readShort(); for (int i = 0; i < attributesCount; i++) { Attribute attr = parseAttribute(); method.addAttribute(attr); if (attr instanceof ExceptionsAttribute) { ExceptionsAttribute exn = (ExceptionsAttribute) attr; ArrayList<String> exnNames = exn.getExceptionList(); if (exnNames.size() > 0) { JClass []exnClasses = new JClass[exnNames.size()]; for (int j = 0; j < exnNames.size(); j++) { String exnName = exnNames.get(j).replace('/', '.'); exnClasses[j] = _loader.forName(exnName); } method.setExceptionTypes(exnClasses); } } } _class.addMethod(method); } /** * Parses an attribute. */ Attribute parseAttribute() throws IOException { int nameIndex = readShort(); String name = _cp.getUtf8(nameIndex).getValue(); if (name.equals("Code")) { CodeAttribute code = new CodeAttribute(name); code.read(this); return code; } else if (name.equals("Exceptions")) { ExceptionsAttribute code = new ExceptionsAttribute(name); code.read(this); return code; } else if (name.equals("Signature")) { SignatureAttribute code = new SignatureAttribute(); code.read(this); return code; } OpaqueAttribute attr = new OpaqueAttribute(name); int length = readInt(); byte []bytes = new byte[length]; read(bytes, 0, bytes.length); attr.setValue(bytes); return attr; } /** * Parses a 64-bit int. */ long readLong() throws IOException { return (((long) _is.read() << 56) | ((long) _is.read() << 48) | ((long) _is.read() << 40) | ((long) _is.read() << 32) | ((long) _is.read() << 24) | ((long) _is.read() << 16) | ((long) _is.read() << 8) | ((long) _is.read())); } /** * Parses a 32-bit int. */ int readInt() throws IOException { return ((_is.read() << 24) | (_is.read() << 16) | (_is.read() << 8) | (_is.read())); } /** * Parses a 16-bit int. */ int readShort() throws IOException { int c1 = _is.read(); int c2 = _is.read(); return ((c1 << 8) | c2); } /** * Parses a byte */ int read() throws IOException { return _is.read(); } /** * Reads a chunk */ int read(byte []buffer, int offset, int length) throws IOException { int readLength = 0; while (length > 0) { int sublen = _is.read(buffer, offset, length); if (sublen < 0) return readLength == 0 ? -1 : readLength; offset += sublen; length -= sublen; readLength += sublen; } return readLength; } /** * Returns an error message. */ private IOException error(String message) { return new IOException(message); } }