Temporarily add BND library code to the build, to try out some patches
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@723235 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java b/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
new file mode 100644
index 0000000..f383915
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
@@ -0,0 +1,755 @@
+/* Copyright 2006 aQute SARL
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+public class Clazz {
+ public static enum QUERY {
+ IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION
+ };
+
+ static protected class Assoc {
+ Assoc(byte tag, int a, int b) {
+ this.tag = tag;
+ this.a = a;
+ this.b = b;
+ }
+
+ byte tag;
+ int a;
+ int b;
+ }
+
+ final static byte SkipTable[] = { 0, // 0 non existent
+ -1, // 1 CONSTANT_utf8 UTF 8, handled in
+ // method
+ -1, // 2
+ 4, // 3 CONSTANT_Integer
+ 4, // 4 CONSTANT_Float
+ 8, // 5 CONSTANT_Long (index +=2!)
+ 8, // 6 CONSTANT_Double (index +=2!)
+ -1, // 7 CONSTANT_Class
+ 2, // 8 CONSTANT_String
+ 4, // 9 CONSTANT_FieldRef
+ 4, // 10 CONSTANT_MethodRef
+ 4, // 11 CONSTANT_InterfaceMethodRef
+ 4, // 12 CONSTANT_NameAndType
+ };
+
+ String className;
+ Object pool[];
+ int intPool[];
+ Map<String, Map<String, String>> imports = new HashMap<String, Map<String, String>>();
+ String path;
+
+ // static String type = "([BCDFIJSZ\\[]|L[^<>]+;)";
+ // static Pattern descriptor = Pattern.compile("\\(" + type + "*\\)(("
+ // + type + ")|V)");
+ int minor = 0;
+ int major = 0;
+
+ String sourceFile;
+ Set<String> xref;
+ Set<Integer> classes;
+ Set<Integer> descriptors;
+ int forName = 0;
+ int class$ = 0;
+ String[] interfaces;
+ String zuper;
+
+ public Clazz(String path) {
+ this.path = path;
+ }
+
+ public Clazz(String path, InputStream in) throws IOException {
+ this.path = path;
+ DataInputStream din = new DataInputStream(in);
+ parseClassFile(din);
+ din.close();
+ }
+
+ Set<String> parseClassFile(DataInputStream in) throws IOException {
+ xref = new HashSet<String>();
+ classes = new HashSet<Integer>();
+ descriptors = new HashSet<Integer>();
+
+ boolean crawl = false; // Crawl the byte code
+ int magic = in.readInt();
+ if (magic != 0xCAFEBABE)
+ throw new IOException("Not a valid class file (no CAFEBABE header)");
+
+ minor = in.readUnsignedShort(); // minor version
+ major = in.readUnsignedShort(); // major version
+ int count = in.readUnsignedShort();
+ pool = new Object[count];
+ intPool = new int[count];
+
+ process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
+ byte tag = in.readByte();
+ switch (tag) {
+ case 0:
+ break process;
+ case 1:
+ constantUtf8(in, poolIndex);
+ break;
+
+ // For some insane optimization reason are
+ // the long and the double two entries in the
+ // constant pool. See 4.4.5
+ case 5:
+ constantLong(in, poolIndex);
+ poolIndex++;
+ break;
+
+ case 6:
+ constantDouble(in, poolIndex);
+ poolIndex++;
+ break;
+
+ case 7:
+ constantClass(in, poolIndex);
+ break;
+
+ case 8:
+ constantString(in, poolIndex);
+ break;
+
+ case 10: // Method ref
+ methodRef(in, poolIndex);
+ break;
+
+ // Name and Type
+ case 12:
+ nameAndType(in, poolIndex, tag);
+ break;
+
+ // We get the skip count for each record type
+ // from the SkipTable. This will also automatically
+ // abort when
+ default:
+ if (tag == 2)
+ throw new IOException("Invalid tag " + tag);
+ in.skipBytes(SkipTable[tag]);
+ break;
+ }
+ }
+
+ pool(pool, intPool);
+ /*
+ * Parse after the constant pool, code thanks to Hans Christian
+ * Falkenberg
+ */
+
+ /* int access_flags = */in.readUnsignedShort(); // access
+ int this_class = in.readUnsignedShort();
+ int super_class = in.readUnsignedShort();
+ zuper = (String) pool[intPool[super_class]];
+ if (zuper != null) {
+ addReference(zuper);
+ }
+ className = (String) pool[intPool[this_class]];
+
+ int interfacesCount = in.readUnsignedShort();
+ if (interfacesCount > 0) {
+ interfaces = new String[interfacesCount];
+ for (int i = 0; i < interfacesCount; i++)
+ interfaces[i] = (String) pool[intPool[in.readUnsignedShort()]];
+ }
+
+ int fieldsCount = in.readUnsignedShort();
+ for (int i = 0; i < fieldsCount; i++) {
+ /* access_flags = */in.readUnsignedShort(); // skip access flags
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+
+ // Java prior to 1.5 used a weird
+ // static variable to hold the com.X.class
+ // result construct. If it did not find it
+ // it would create a variable class$com$X
+ // that would be used to hold the class
+ // object gotten with Class.forName ...
+ // Stupidly, they did not actively use the
+ // class name for the field type, so bnd
+ // would not see a reference. We detect
+ // this case and add an artificial descriptor
+ String name = pool[name_index].toString(); // name_index
+ if (name.startsWith("class$")) {
+ crawl = true;
+ }
+
+ descriptors.add(new Integer(descriptor_index));
+ doAttributes(in, false);
+ }
+
+ //
+ // Check if we have to crawl the code to find
+ // the ldc(_w) <string constant> invokestatic Class.forName
+ // if so, calculate the method ref index so we
+ // can do this efficiently
+ //
+ if (crawl) {
+ forName = findMethod("java/lang/Class", "forName",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ class$ = findMethod(className, "class$",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ }
+
+ //
+ // Handle the methods
+ //
+ int methodCount = in.readUnsignedShort();
+ for (int i = 0; i < methodCount; i++) {
+ /* access_flags = */in.readUnsignedShort();
+ /* int name_index = */in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ // String s = (String) pool[name_index];
+ descriptors.add(new Integer(descriptor_index));
+ doAttributes(in, crawl);
+ }
+
+ doAttributes(in, false);
+
+ //
+ // Now iterate over all classes we found and
+ // parse those as well. We skip duplicates
+ //
+
+ for (Iterator<Integer> e = classes.iterator(); e.hasNext();) {
+ int class_index = e.next().shortValue();
+ doClassReference((String) pool[class_index]);
+ }
+
+ //
+ // Parse all the descriptors we found
+ //
+
+ for (Iterator<Integer> e = descriptors.iterator(); e.hasNext();) {
+ Integer index = e.next();
+ String prototype = (String) pool[index.intValue()];
+ if (prototype != null)
+ parseDescriptor(prototype);
+ else
+ System.err.println("Unrecognized descriptor: " + index);
+ }
+ Set<String> xref = this.xref;
+ reset();
+ return xref;
+ }
+
+ protected void pool(Object[] pool, int[] intPool) {
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @param tag
+ * @throws IOException
+ */
+ protected void nameAndType(DataInputStream in, int poolIndex, byte tag)
+ throws IOException {
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ descriptors.add(new Integer(descriptor_index));
+ pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @param tag
+ * @throws IOException
+ */
+ private void methodRef(DataInputStream in, int poolIndex)
+ throws IOException {
+ int class_index = in.readUnsignedShort();
+ int name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ private void constantString(DataInputStream in, int poolIndex)
+ throws IOException {
+ int string_index = in.readUnsignedShort();
+ intPool[poolIndex] = string_index;
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ protected void constantClass(DataInputStream in, int poolIndex)
+ throws IOException {
+ int class_index = in.readUnsignedShort();
+ classes.add(new Integer(class_index));
+ intPool[poolIndex] = class_index;
+ }
+
+ /**
+ * @param in
+ * @throws IOException
+ */
+ protected void constantDouble(DataInputStream in, int poolIndex)
+ throws IOException {
+ in.skipBytes(8);
+ }
+
+ /**
+ * @param in
+ * @throws IOException
+ */
+ protected void constantLong(DataInputStream in, int poolIndex)
+ throws IOException {
+ in.skipBytes(8);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ protected void constantUtf8(DataInputStream in, int poolIndex)
+ throws IOException {
+ // CONSTANT_Utf8
+
+ String name = in.readUTF();
+ xref.add(name);
+ pool[poolIndex] = name;
+ }
+
+ /**
+ * Find a method reference in the pool that points to the given class,
+ * methodname and descriptor.
+ *
+ * @param clazz
+ * @param methodname
+ * @param descriptor
+ * @return index in constant pool
+ */
+ private int findMethod(String clazz, String methodname, String descriptor) {
+ for (int i = 1; i < pool.length; i++) {
+ if (pool[i] instanceof Assoc) {
+ Assoc methodref = (Assoc) pool[i];
+ if (methodref.tag == 10) {
+ // Method ref
+ int class_index = methodref.a;
+ int class_name_index = intPool[class_index];
+ if (clazz.equals(pool[class_name_index])) {
+ int name_and_type_index = methodref.b;
+ Assoc name_and_type = (Assoc) pool[name_and_type_index];
+ if (name_and_type.tag == 12) {
+ // Name and Type
+ int name_index = name_and_type.a;
+ int type_index = name_and_type.b;
+ if (methodname.equals(pool[name_index])) {
+ if (descriptor.equals(pool[type_index])) {
+ return i;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ private void doClassReference(String next) {
+ if (next != null) {
+ String normalized = normalize(next);
+ if (normalized != null) {
+ String pack = getPackage(normalized);
+ packageReference(pack);
+ }
+ } else
+ throw new IllegalArgumentException("Invalid class, parent=");
+ }
+
+ /**
+ * Called for each attribute in the class, field, or method.
+ *
+ * @param in
+ * The stream
+ * @throws IOException
+ */
+ private void doAttributes(DataInputStream in, boolean crawl)
+ throws IOException {
+ int attributesCount = in.readUnsignedShort();
+ for (int j = 0; j < attributesCount; j++) {
+ // skip name CONSTANT_Utf8 pointer
+ doAttribute(in, crawl);
+ }
+ }
+
+ /**
+ * Process a single attribute, if not recognized, skip it.
+ *
+ * @param in
+ * the data stream
+ * @throws IOException
+ */
+ private void doAttribute(DataInputStream in, boolean crawl)
+ throws IOException {
+ int attribute_name_index = in.readUnsignedShort();
+ String attributeName = (String) pool[attribute_name_index];
+ if (attribute_name_index == 560)
+ System.out.println("Index " + attribute_name_index + ":"
+ + attributeName);
+ long attribute_length = in.readInt();
+ attribute_length &= 0xFFFFFFFF;
+ if ("RuntimeVisibleAnnotations".equals(attributeName))
+ doAnnotations(in);
+ else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
+ doParameterAnnotations(in);
+ else if ("SourceFile".equals(attributeName))
+ doSourceFile(in);
+ else if ("Code".equals(attributeName) && crawl)
+ doCode(in);
+ else {
+ if (attribute_length > 0x7FFFFFFF) {
+ throw new IllegalArgumentException("Attribute > 2Gb");
+ }
+ in.skipBytes((int) attribute_length);
+ }
+ }
+
+ /**
+ * <pre>
+ * Code_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 max_stack;
+ * u2 max_locals;
+ * u4 code_length;
+ * u1 code[code_length];
+ * u2 exception_table_length;
+ * { u2 start_pc;
+ * u2 end_pc;
+ * u2 handler_pc;
+ * u2 catch_type;
+ * } exception_table[exception_table_length];
+ * u2 attributes_count;
+ * attribute_info attributes[attributes_count];
+ * }
+ * </pre>
+ *
+ * @param in
+ * @param pool
+ * @throws IOException
+ */
+ private void doCode(DataInputStream in) throws IOException {
+ /* int max_stack = */in.readUnsignedShort();
+ /* int max_locals = */in.readUnsignedShort();
+ int code_length = in.readInt();
+ byte code[] = new byte[code_length];
+ in.readFully(code);
+ crawl(code);
+ int exception_table_length = in.readUnsignedShort();
+ in.skipBytes(exception_table_length * 8);
+ doAttributes(in, false);
+ }
+
+ /**
+ * We must find Class.forName references ...
+ *
+ * @param code
+ */
+ protected void crawl(byte[] code) {
+ ByteBuffer bb = ByteBuffer.wrap(code);
+ bb.order(ByteOrder.BIG_ENDIAN);
+ int lastReference = -1;
+
+ while (bb.remaining() > 0) {
+ int instruction = 0xFF & bb.get();
+ switch (instruction) {
+ case OpCodes.ldc:
+ lastReference = 0xFF & bb.get();
+ break;
+
+ case OpCodes.ldc_w:
+ lastReference = 0xFFFF & bb.getShort();
+ break;
+
+ case OpCodes.invokestatic:
+ int methodref = 0xFFFF & bb.getShort();
+ if ((methodref == forName || methodref == class$)
+ && lastReference != -1
+ && pool[intPool[lastReference]] instanceof String) {
+ String clazz = (String) pool[intPool[lastReference]];
+ doClassReference(clazz.replace('.', '/'));
+ }
+ break;
+
+ case OpCodes.tableswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ bb.get();
+ /* int deflt = */
+ bb.getInt();
+ int low = bb.getInt();
+ int high = bb.getInt();
+ bb.position(bb.position() + (high - low + 1) * 4);
+ lastReference = -1;
+ break;
+
+ case OpCodes.lookupswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ bb.get();
+ /* deflt = */
+ bb.getInt();
+ int npairs = bb.getInt();
+ bb.position(bb.position() + npairs * 8);
+ lastReference = -1;
+ break;
+
+ default:
+ lastReference = -1;
+ bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
+ }
+ }
+ }
+
+ private void doSourceFile(DataInputStream in) throws IOException {
+ int sourcefile_index = in.readUnsignedShort();
+ this.sourceFile = pool[sourcefile_index].toString();
+ }
+
+ private void doParameterAnnotations(DataInputStream in) throws IOException {
+ int num_parameters = in.readUnsignedByte();
+ for (int p = 0; p < num_parameters; p++) {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ for (int a = 0; a < num_annotations; a++) {
+ doAnnotation(in);
+ }
+ }
+ }
+
+ private void doAnnotations(DataInputStream in) throws IOException {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ for (int a = 0; a < num_annotations; a++) {
+ doAnnotation(in);
+ }
+ }
+
+ private void doAnnotation(DataInputStream in) throws IOException {
+ int type_index = in.readUnsignedShort();
+ descriptors.add(new Integer(type_index));
+ int num_element_value_pairs = in.readUnsignedShort();
+ for (int v = 0; v < num_element_value_pairs; v++) {
+ /* int element_name_index = */in.readUnsignedShort();
+ doElementValue(in);
+ }
+ }
+
+ private void doElementValue(DataInputStream in) throws IOException {
+ int tag = in.readUnsignedByte();
+ switch (tag) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ case 's':
+ /* int const_value_index = */
+ in.readUnsignedShort();
+ break;
+
+ case 'e':
+ int type_name_index = in.readUnsignedShort();
+ descriptors.add(new Integer(type_name_index));
+ /* int const_name_index = */
+ in.readUnsignedShort();
+ break;
+
+ case 'c':
+ int class_info_index = in.readUnsignedShort();
+ descriptors.add(new Integer(class_info_index));
+ break;
+
+ case '@':
+ doAnnotation(in);
+ break;
+
+ case '[':
+ int num_values = in.readUnsignedShort();
+ for (int i = 0; i < num_values; i++) {
+ doElementValue(in);
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "Invalid value for Annotation ElementValue tag " + tag);
+ }
+ }
+
+ void packageReference(String pack) {
+ if (pack.indexOf('<') >= 0)
+ System.out.println("Oops: " + pack);
+ if (!imports.containsKey(pack))
+ imports.put(pack, new LinkedHashMap<String, String>());
+ }
+
+ void parseDescriptor(String prototype) {
+ addReference(prototype);
+ StringTokenizer st = new StringTokenizer(prototype, "(;)", true);
+ while (st.hasMoreTokens()) {
+ if (st.nextToken().equals("(")) {
+ String token = st.nextToken();
+ while (!token.equals(")")) {
+ addReference(token);
+ token = st.nextToken();
+ }
+ token = st.nextToken();
+ addReference(token);
+ }
+ }
+ }
+
+ private void addReference(String token) {
+ while (token.startsWith("["))
+ token = token.substring(1);
+
+ if (token.startsWith("L")) {
+ String clazz = normalize(token.substring(1));
+ if (clazz.startsWith("java/"))
+ return;
+ String pack = getPackage(clazz);
+ packageReference(pack);
+ }
+ }
+
+ static String normalize(String s) {
+ if (s.startsWith("[L"))
+ return normalize(s.substring(2));
+ if (s.startsWith("["))
+ if (s.length() == 2)
+ return null;
+ else
+ return normalize(s.substring(1));
+ if (s.endsWith(";"))
+ return normalize(s.substring(0, s.length() - 1));
+ return s + ".class";
+ }
+
+ public static String getPackage(String clazz) {
+ int n = clazz.lastIndexOf('/');
+ if (n < 0)
+ return ".";
+ return clazz.substring(0, n).replace('/', '.');
+ }
+
+ public Map<String, Map<String, String>> getReferred() {
+ return imports;
+ }
+
+ String getClassName() {
+ return className;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Set<String> xref(InputStream in) throws IOException {
+ DataInputStream din = new DataInputStream(in);
+ Set<String> set = parseClassFile(din);
+ din.close();
+ return set;
+ }
+
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ /**
+ * .class construct for different compilers
+ *
+ * sun 1.1 Detect static variable class$com$acme$MyClass 1.2 " 1.3 " 1.4 "
+ * 1.5 ldc_w (class) 1.6 "
+ *
+ * eclipse 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 "
+ * 1.5 ldc (class) 1.6 "
+ *
+ * 1.5 and later is not an issue, sun pre 1.5 is easy to detect the static
+ * variable that decodes the class name. For eclipse, the class$0 gives away
+ * we have a reference encoded in a string.
+ * compilerversions/compilerversions.jar contains test versions of all
+ * versions/compilers.
+ */
+
+ public void reset() {
+ pool = null;
+ intPool = null;
+ xref = null;
+ classes = null;
+ descriptors = null;
+ }
+
+ public boolean is(QUERY query, Instruction instr, Map<String, Clazz> classspace) {
+ switch (query) {
+ case ANY:
+ return true;
+
+ case NAMED:
+ if ( instr.matches(getClassName()))
+ return !instr.isNegated();
+ return false;
+
+ case VERSION:
+ String v = major + "/" + minor;
+ if ( instr.matches(v))
+ return !instr.isNegated();
+ return false;
+
+
+ case IMPLEMENTS:
+ for ( int i=0; interfaces != null && i<interfaces.length; i++ ) {
+ if ( instr.matches(interfaces[i]))
+ return !instr.isNegated();
+ }
+ break;
+ case EXTENDS:
+ if ( zuper == null )
+ return false;
+
+ if ( instr.matches(zuper))
+ return !instr.isNegated();
+ break;
+
+ case IMPORTS:
+ for ( String imp : imports.keySet() ) {
+ if ( instr.matches(imp.replace('.', '/')))
+ return !instr.isNegated();
+ }
+ }
+
+ if ( zuper == null || classspace == null)
+ return false;
+
+ Clazz clazz = classspace.get(zuper);
+ if (clazz == null)
+ return false;
+
+ return clazz.is(query, instr, classspace);
+ }
+
+ public String toString() {
+ return getFQN();
+ }
+
+ public String getFQN() {
+ return getClassName().replace('/', '.');
+ }
+}