com.caucho.bytecode.ByteCodeClassScanner Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 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 java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.inject.Module;
import com.caucho.util.L10N;
/**
* Scans for matching classes.
*/
@Module
public class ByteCodeClassScanner {
private static final Logger log
= Logger.getLogger(ByteCodeClassScanner.class.getName());
private static final L10N L = new L10N(ByteCodeClassScanner.class);
private static final char []RUNTIME_VISIBLE_ANNOTATIONS
= "RuntimeVisibleAnnotations".toCharArray();
private static final boolean []IS_JAVA_IDENTIFIER;
private String _className;
private InputStream _is;
private ByteCodeClassMatcher _matcher;
private char []_charBuffer;
private int _charBufferOffset;
private int _cpCount;
private int []_cpData;
private int []_cpLengths;
private int []_classData;
public ByteCodeClassScanner()
{
_charBuffer = new char[16384];
}
public void init(String className,
InputStream is,
ByteCodeClassMatcher matcher)
{
_className = className;
_is = is;
_matcher = matcher;
_charBufferOffset = 0;
}
public boolean scan()
{
try {
InputStream is = _is;
int magic = readInt(is);
if (magic != JavaClass.MAGIC)
throw error(L.l("bad magic number in class file"));
is.skip(2); // major
is.skip(2); // minor
parseConstantPool(is);
int modifiers = readShort(is);
int thisClassIndex = readShort(is);
int cpIndex = _classData[thisClassIndex];
if (cpIndex > 0) {
String className = new String(_charBuffer,
_cpData[cpIndex],
_cpLengths[cpIndex]);
if (! _matcher.scanClass(className, modifiers)) {
return false;
}
}
int superClassIndex = readShort(is);
if (superClassIndex > 0) {
cpIndex = _classData[superClassIndex];
if (cpIndex > 0) {
_matcher.addSuperClass(_charBuffer,
_cpData[cpIndex],
_cpLengths[cpIndex]);
}
}
int interfaceCount = readShort(is);
for (int i = 0; i < interfaceCount; i++) {
int classIndex = readShort(is);
cpIndex = _classData[classIndex];
if (cpIndex > 0) {
_matcher.addInterface(_charBuffer,
_cpData[cpIndex],
_cpLengths[cpIndex]);
}
}
int fieldCount = readShort(is);
for (int i = 0; i < fieldCount; i++) {
scanField(is);
}
int methodCount = readShort(is);
for (int i = 0; i < methodCount; i++) {
scanMethod(is);
}
int attrCount = readShort(is);
for (int i = 0; i < attrCount; i++) {
scanClassAttribute(is);
}
char []charBuffer = _charBuffer;
for (int i = 0; i < _cpCount; i++) {
int cpLength = _cpLengths[i];
if (cpLength > 0) {
int cpOffset = _cpData[i];
if (charBuffer[cpOffset] == 'L'
&& charBuffer[cpOffset + cpLength - 1] == ';') {
_matcher.addPoolString(charBuffer,
cpOffset + 1,
cpLength - 2);
}
}
}
return _matcher.finishScan();
} catch (Exception e) {
log.log(Level.WARNING,
"failed scanning class " + _className + "\n" + e.toString(),
e);
return false;
}
}
/**
* Parses the constant pool.
*/
public boolean parseConstantPool(InputStream is)
throws IOException
{
int count = readShort(is);
_cpCount = count;
if (_cpData == null || _cpData.length <= count) {
_cpData = new int[count];
_cpLengths = new int[count];
_classData = new int[count];
}
Arrays.fill(_cpData, 0);
Arrays.fill(_cpLengths, 0);
Arrays.fill(_classData, 0);
int i = 1;
while (i < count) {
int index = i;
int code = is.read();
if (code == ByteCodeParser.CP_LONG || code == ByteCodeParser.CP_DOUBLE)
i += 2;
else
i += 1;
switch (code) {
case ByteCodeParser.CP_CLASS:
int utf8Index = readShort(is);
// index of the UTF-8 string
_classData[index] = utf8Index;
break;
case ByteCodeParser.CP_FIELD_REF:
// int classIndex = readShort();
// int nameAndTypeIndex = readShort();
is.skip(4);
break;
case ByteCodeParser.CP_METHOD_REF:
// int classIndex = readShort();
// int nameAndTypeIndex = readShort();
is.skip(4);
break;
case ByteCodeParser.CP_INTERFACE_METHOD_REF:
// int classIndex = readShort();
// int nameAndTypeIndex = readShort();
is.skip(4);
break;
case ByteCodeParser.CP_STRING:
// int stringIndex = readShort();
is.skip(2);
break;
case ByteCodeParser.CP_INTEGER:
is.skip(4);
break;
case ByteCodeParser.CP_FLOAT:
is.skip(4);
break;
case ByteCodeParser.CP_LONG:
is.skip(8);
break;
case ByteCodeParser.CP_DOUBLE:
is.skip(8);
break;
case ByteCodeParser.CP_NAME_AND_TYPE:
// int nameIndex = readShort();
// int descriptorIndex = readShort();
is.skip(4);
break;
case ByteCodeParser.CP_UTF8:
{
int length = readShort(is);
_cpData[index] = _charBufferOffset;
_cpLengths[index] = parseUtf8(is, length);
break;
}
case ByteCodeParser.CP_METHOD_HANDLE:
is.skip(3);
break;
case ByteCodeParser.CP_METHOD_TYPE:
is.skip(2);
break;
case ByteCodeParser.CP_INVOKE_DYNAMIC:
is.skip(4);
break;
default:
throw error(L.l("'{0}' is an unknown constant pool type.", code));
}
}
return false;
}
/**
* Parses the UTF.
*/
private int parseUtf8(InputStream is, int length)
throws IOException
{
if (length <= 0)
return 0;
if (length > 256) {
is.skip(length);
return 0;
}
int offset = _charBufferOffset;
if (_charBuffer.length <= offset + length) {
char []buffer = new char[2 * _charBuffer.length];
System.arraycopy(_charBuffer, 0, buffer, 0, _charBuffer.length);
_charBuffer = buffer;
}
char []buffer = _charBuffer;
boolean []isJavaIdentifier = IS_JAVA_IDENTIFIER;
boolean isIdentifier = true;
while (length > 0) {
int d1 = is.read();
char ch;
if (d1 == '/') {
ch = '.';
length--;
}
else if (d1 < 0x80) {
ch = (char) d1;
length--;
}
else if (d1 < 0xe0) {
int d2 = is.read() & 0x3f;
ch = (char) (((d1 & 0x1f) << 6) + (d2));
length -= 2;
}
else if (d1 < 0xf0) {
int d2 = is.read() & 0x3f;
int d3 = is.read() & 0x3f;
ch = (char) (((d1 & 0xf) << 12) + (d2 << 6) + d3);
length -= 3;
}
else {
throw error("invalid UTF-8 code: 0x" + Integer.toHexString(d1));
}
if (isIdentifier && isJavaIdentifier[ch]) {
buffer[offset++] = ch;
}
else {
isIdentifier = false;
}
}
if (! isIdentifier)
return 0;
int charLength = offset - _charBufferOffset;
_charBufferOffset = offset;
return charLength;
}
/**
* Parses a field entry.
*/
private void scanField(InputStream is)
throws IOException
{
// int accessFlags = readShort();
// int nameIndex = readShort();
// int descriptorIndex = readShort();
is.skip(6);
int attributesCount = readShort(is);
for (int i = 0; i < attributesCount; i++) {
scanAttributeForAnnotation(is);
}
}
/**
* Parses a method entry.
*/
private void scanMethod(InputStream is)
throws IOException
{
/*
int accessFlags = readShort();
int nameIndex = readShort();
int descriptorIndex = readShort();
*/
is.skip(6);
int attributesCount = readShort(is);
for (int i = 0; i < attributesCount; i++) {
scanAttributeForAnnotation(is);
}
}
/**
* Parses an attribute.
*/
private void scanClassAttribute(InputStream is)
throws IOException
{
int nameIndex = readShort(is);
// String name = _cp.getUtf8(nameIndex).getValue();
int length = readInt(is);
if (! isNameAnnotation(nameIndex)) {
is.skip(length);
return;
}
int count = readShort(is);
for (int i = 0; i < count; i++) {
int annTypeIndex = scanAnnotation(is);
if (annTypeIndex > 0) {
_matcher.addClassAnnotation(_charBuffer,
_cpData[annTypeIndex] + 1,
_cpLengths[annTypeIndex] - 2);
}
}
}
/**
* Parses an attribute for an annotation.
*/
private void scanAttributeForAnnotation(InputStream is)
throws IOException
{
int nameIndex = readShort(is);
// String name = _cp.getUtf8(nameIndex).getValue();
int length = readInt(is);
if (! isNameAnnotation(nameIndex)) {
is.skip(length);
return;
}
int count = readShort(is);
for (int i = 0; i < count; i++) {
int annTypeIndex = scanAnnotation(is);
if (annTypeIndex > 0 && _cpLengths[annTypeIndex] > 2) {
_matcher.addClassAnnotation(_charBuffer,
_cpData[annTypeIndex] + 1,
_cpLengths[annTypeIndex] - 2);
}
}
}
private int scanAnnotation(InputStream is)
throws IOException
{
int typeIndex = readShort(is);
int valueCount = readShort(is);
for (int j = 0; j < valueCount; j++) {
is.skip(2); // int eltIndex = readShort(is);
skipElementValue(is);
}
return typeIndex;
}
private void skipElementValue(InputStream is)
throws IOException
{
int code = is.read();
switch (code) {
case 'B': case 'C': case 'D': case 'F': case 'I': case 'J':
case 'S': case 'Z': case 's':
is.skip(2);
return;
case 'e':
is.skip(4);
return;
case 'c':
is.skip(2);
return;
case '@':
scanAnnotation(is);
return;
case '[':
int len = readShort(is);
for (int i = 0; i < len; i++) {
skipElementValue(is);
}
return;
default:
throw error("unknown code: " + (char) code);
}
}
private boolean isNameAnnotation(int nameIndex)
{
if (nameIndex <= 0)
return false;
int offset = _cpData[nameIndex];
int length = _cpLengths[nameIndex];
char []charBuffer = _charBuffer;
if (length != RUNTIME_VISIBLE_ANNOTATIONS.length)
return false;
for (int i = 0; i < length; i++) {
if (charBuffer[i + offset] != RUNTIME_VISIBLE_ANNOTATIONS[i])
return false;
}
return true;
}
/**
* Parses a 32-bit int.
*/
private int readInt(InputStream is)
throws IOException
{
return ((is.read() << 24)
| (is.read() << 16)
| (is.read() << 8)
| (is.read()));
}
/**
* Parses a 16-bit int.
*/
private int readShort(InputStream is)
throws IOException
{
int c1 = is.read();
int c2 = is.read();
return ((c1 << 8) | c2);
}
/**
* Returns an error message.
*/
private IllegalStateException error(String message)
{
return new IllegalStateException(_className + ": " + message + " [" + _is + "]");
}
static {
IS_JAVA_IDENTIFIER = new boolean[65536];
for (int i = 0; i < 65536; i++) {
if (Character.isJavaIdentifierPart(i) || i == '.' || i == ';')
IS_JAVA_IDENTIFIER[i] = true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy