All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.lib.osgi.Clazz Maven / Gradle / Ivy

There is a newer version: 2.0.0.20130123-133441
Show newest version
package aQute.lib.osgi;

import java.io.*;
import java.lang.annotation.*;
import java.nio.*;
import java.util.*;
import java.util.regex.*;

import aQute.bnd.annotation.*;
import aQute.libg.generics.*;

public class Clazz {

	public class ClassConstant {
		int	cname;

		public ClassConstant(int class_index) {
			this.cname = class_index;
		}

		public String getName() {
			return (String) pool[cname];
		}
	}

	public static enum JAVA {
		UNKNOWN(Integer.MAX_VALUE), OpenJDK7(51), J2S6(50), J2SE5(49), JDK1_4(48), JDK1_3(47), JDK1_2(
				46), JDK1_1(45);

		final int	major;

		JAVA(int major) {
			this.major = major;
		}

		static JAVA format(int n) {
			for (JAVA e : JAVA.values())
				if (e.major == n)
					return e;
			return UNKNOWN;
		}

		public int getMajor() {
			return major;
		}

		public boolean hasAnnotations() {
			return major >= J2SE5.major;
		}

		public boolean hasGenerics() {
			return major >= J2SE5.major;
		}

		public boolean hasEnums() {
			return major >= J2SE5.major;
		}
	};

	public static enum QUERY {
		IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION, CONCRETE, ABSTRACT, PUBLIC, ANNOTATION, RUNTIMEANNOTATIONS, CLASSANNOTATIONS
	};

	public static EnumSet	HAS_ARGUMENT	= EnumSet.of(QUERY.IMPLEMENTS, QUERY.EXTENDS,
															QUERY.IMPORTS, QUERY.NAMED,
															QUERY.VERSION, QUERY.ANNOTATION);

	/**
	 * 
	 * ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its
	 * package. 
	 * ACC_FINAL 0x0010 Declared final; no subclasses allowed.
	 * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
	 * invokespecial instruction. 
	 * ACC_INTERFACE 0x0200 Is an interface, not a
	 * class. 
	 * ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated.
	 * 
* * @param mod */ final static int ACC_PUBLIC = 0x0001; // Declared // public; // may // be // accessed // from outside its package. final static int ACC_FINAL = 0x0010; // Declared // final; // no // subclasses // allowed. final static int ACC_SUPER = 0x0020; // Treat // superclass // methods // specially when invoked by the // invokespecial instruction. final static int ACC_INTERFACE = 0x0200; // Is // an // interface, // not // a // classs final static int ACC_ABSTRACT = 0x0400; // Declared // abstract; // may // not // be // instantiated. final static int ACC_ENUM = 0x04000; 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; } static public class FieldDef implements Comparable { public FieldDef(int access, String clazz, String name, String descriptor) { this.access = access; this.clazz = clazz.replace('/', '.'); this.name = name; this.descriptor = descriptor; } final public int access; final public String clazz; final public String name; final public String descriptor; public String signature; public Object constant; public boolean equals(Object other) { if (!(other instanceof MethodDef)) return false; FieldDef m = (FieldDef) other; return clazz.equals(m.clazz) && name.equals(m.name) && descriptor.equals(m.descriptor); } public int hashCode() { return clazz.hashCode() ^ name.hashCode() ^ descriptor.hashCode(); } public int compareTo(FieldDef o) { int result = clazz.compareTo(o.clazz); if (result == 0) { result = name.compareTo(o.name); if (result == 0) { result = descriptor.compareTo(o.descriptor); } } return result; } public String getPretty() { return name; } public String toString() { return getPretty(); } public boolean isEnum() { return (access & ACC_ENUM) != 0; } } static public class MethodDef extends FieldDef { Pattern METHOD_DESCRIPTOR = Pattern.compile("\\((.*)\\)(.+)"); public MethodDef(int access, String clazz, String method, String descriptor) { super(access, clazz, method, descriptor); } public boolean isConstructor() { return name.equals("") || name.equals(""); } public String getReturnType() { String use = descriptor; if (signature != null) use = signature; Matcher m = METHOD_DESCRIPTOR.matcher(use); if (!m.matches()) throw new IllegalArgumentException("Not a valid method descriptor: " + descriptor); String returnType = m.group(2); return objectDescriptorToFQN(returnType); } public String getPretty() { StringBuilder sb = new StringBuilder(); sb.append(descriptor.charAt(0)); int index = 1; String del = ""; while (index < descriptor.length() && descriptor.charAt(index) != ')') { sb.append(del); index = printParameter(sb, descriptor, index); del = ","; } sb.append(descriptor.charAt(index++)); StringBuilder sb2 = new StringBuilder(); if (isConstructor()) { sb2.append(getShortName(clazz)); index++; // skip the V } else { printParameter(sb2, descriptor, index); sb2.append(" "); sb2.append(getShortName(clazz)); sb2.append("."); sb2.append(name); } sb2.append(sb); return sb2.toString(); } private int printParameter(StringBuilder sb, CharSequence descriptor, int index) { char c = descriptor.charAt(index++); switch (c) { case 'B': sb.append("byte"); break; case 'C': sb.append("char"); break; case 'D': sb.append("double"); break; case 'F': sb.append("float"); break; case 'I': sb.append("int"); break; case 'J': sb.append("long"); break; case 'S': sb.append("short"); break; case 'Z': sb.append("boolean"); break; case 'V': sb.append("void"); break; case 'L': index = reference(sb, descriptor, index); break; case '[': index = array(sb, descriptor, index); break; } return index; } private int reference(StringBuilder sb, CharSequence descriptor, int index) { int n = sb.length(); int lastSlash = n; while (index < descriptor.length() && descriptor.charAt(index) != ';') { char c = descriptor.charAt(index++); if (c == '/') { c = '.'; lastSlash = sb.length() + 1; } sb.append(c); } if (lastSlash != n) { sb.delete(n, lastSlash); } return ++index; } private int array(StringBuilder sb, CharSequence descriptor, int index) { int n = 1; while (index < descriptor.length() && descriptor.charAt(index) == '[') { index++; } index = printParameter(sb, descriptor, index); while (n-- > 0) { sb.append("[]"); } return index; } } 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 }; boolean isAbstract; boolean isPublic; boolean isEnum; boolean hasRuntimeAnnotations; boolean hasClassAnnotations; String className; Object pool[]; int intPool[]; Set imports = Create.set(); String path; int minor = 0; int major = 0; int access = 0; String sourceFile; Set xref; Set classes; Set descriptors; Set annotations; int forName = 0; int class$ = 0; String[] interfaces; String zuper; ClassDataCollector cd = null; Resource resource; FieldDef last = null; public Clazz(String path, Resource resource) { this.path = path; this.resource = resource; } public Set parseClassFile() throws Exception { return parseClassFileWithCollector(null); } public Set parseClassFile(InputStream in) throws IOException { return parseClassFile(in, null); } public Set parseClassFileWithCollector(ClassDataCollector cd) throws Exception { InputStream in = resource.openInputStream(); try { return parseClassFile(in, cd); } finally { in.close(); } } public Set parseClassFile(InputStream in, ClassDataCollector cd) throws IOException { DataInputStream din = new DataInputStream(in); try { this.cd = cd; return parseClassFile(din); } finally { cd = null; din.close(); } } Set parseClassFile(DataInputStream in) throws IOException { xref = new HashSet(); classes = new HashSet(); descriptors = new HashSet(); boolean crawl = cd != null; // Crawl the byte code if we have a // collector 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; case 3: constantInteger(in, poolIndex); break; case 4: constantFloat(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 case 11: // Interface 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 isAbstract = (access_flags & ACC_ABSTRACT) != 0; isPublic = (access_flags & ACC_PUBLIC) != 0; isEnum = (access_flags & ACC_ENUM) != 0; int this_class = in.readUnsignedShort(); className = (String) pool[intPool[this_class]]; try { if (cd != null) { if (!cd.classStart(access_flags, className)) return null; } int super_class = in.readUnsignedShort(); zuper = (String) pool[intPool[super_class]]; if (zuper != null) { String pack = getPackage(zuper); packageReference(pack); if (cd != null) cd.extendsClass(zuper); } 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()]]; if (cd != null) cd.implementsInterfaces(interfaces); } 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; } if (cd != null) cd.field(last = new FieldDef(access_flags, className, name, pool[descriptor_index].toString())); descriptors.add(new Integer(descriptor_index)); doAttributes(in, ElementType.FIELD, false); } // // Check if we have to crawl the code to find // the ldc(_w) invokestatic Class.forName // if so, calculate the method ref index so we // can do this efficiently // if (crawl) { forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); class$ = findMethodReference(className, "class$", "(Ljava/lang/String;)Ljava/lang/Class;"); } else if (major == 48) { forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); if (forName > 0) { crawl = true; class$ = findMethodReference(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(); descriptors.add(new Integer(descriptor_index)); String name = pool[name_index].toString(); String descriptor = pool[descriptor_index].toString(); if (cd != null) { MethodDef mdef = new MethodDef(access_flags, className, name, descriptor); last = mdef; cd.method(mdef); } if ("".equals(name)) { doAttributes(in, ElementType.CONSTRUCTOR, crawl); } else { doAttributes(in, ElementType.METHOD, crawl); } } doAttributes(in, ElementType.TYPE, false); // // Now iterate over all classes we found and // parse those as well. We skip duplicates // for (int n : classes) { String clazz = (String) pool[n]; if (clazz.endsWith(";") || clazz.startsWith("[")) parseReference(clazz, 0); else { String pack = getPackage(clazz); packageReference(pack); } } // // Parse all the descriptors we found // for (Iterator 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 xref = this.xref; reset(); return xref; } finally { if (cd != null) cd.classEnd(); } } private void constantFloat(DataInputStream in, int poolIndex) throws IOException { if (cd != null) pool[poolIndex] = in.readFloat(); // ALU else in.skipBytes(4); } private void constantInteger(DataInputStream in, int poolIndex) throws IOException { intPool[poolIndex] = in.readInt(); if (cd != null) pool[poolIndex] = intPool[poolIndex]; } 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; ClassConstant c = new ClassConstant(class_index); pool[poolIndex] = c; } /** * @param in * @throws IOException */ protected void constantDouble(DataInputStream in, int poolIndex) throws IOException { if (cd != null) pool[poolIndex] = in.readDouble(); else in.skipBytes(8); } /** * @param in * @throws IOException */ protected void constantLong(DataInputStream in, int poolIndex) throws IOException { if (cd != null) { pool[poolIndex] = in.readLong(); } else 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 findMethodReference(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; } /** * Called for each attribute in the class, field, or method. * * @param in * The stream * @throws IOException */ private void doAttributes(DataInputStream in, ElementType member, boolean crawl) throws IOException { int attributesCount = in.readUnsignedShort(); for (int j = 0; j < attributesCount; j++) { // skip name CONSTANT_Utf8 pointer doAttribute(in, member, crawl); } } /** * Process a single attribute, if not recognized, skip it. * * @param in * the data stream * @throws IOException */ private void doAttribute(DataInputStream in, ElementType member, boolean crawl) throws IOException { int attribute_name_index = in.readUnsignedShort(); String attributeName = (String) pool[attribute_name_index]; long attribute_length = in.readInt(); attribute_length &= 0xFFFFFFFF; if ("RuntimeVisibleAnnotations".equals(attributeName)) doAnnotations(in, member, RetentionPolicy.RUNTIME); else if ("RuntimeVisibleParameterAnnotations".equals(attributeName)) doParameterAnnotations(in, member, RetentionPolicy.RUNTIME); else if ("RuntimeInvisibleAnnotations".equals(attributeName)) doAnnotations(in, member, RetentionPolicy.CLASS); else if ("RuntimeInvisibleParameterAnnotations".equals(attributeName)) doParameterAnnotations(in, member, RetentionPolicy.CLASS); else if ("InnerClasses".equals(attributeName)) doInnerClasses(in); else if ("EnclosingMethod".equals(attributeName)) doEnclosingMethod(in); else if ("SourceFile".equals(attributeName)) doSourceFile(in); else if ("Code".equals(attributeName) && crawl) doCode(in); else if ("Signature".equals(attributeName)) doSignature(in, member); else if ("ConstantValue".equals(attributeName)) doConstantValue(in); else { if (attribute_length > 0x7FFFFFFF) { throw new IllegalArgumentException("Attribute > 2Gb"); } in.skipBytes((int) attribute_length); } } /** *
	 * EnclosingMethod_attribute { 
	 * 	u2 attribute_name_index; 
	 * 	u4 attribute_length; 
	 * 	u2 class_index
	 * 	u2 method_index;
	 * }
	 * 
* * * @param in * @throws IOException */ private void doEnclosingMethod(DataInputStream in) throws IOException { int cIndex = in.readShort(); int mIndex = in.readShort(); if (cd != null) { int nameIndex = intPool[cIndex]; String cName = (String) pool[nameIndex]; String mName = null; String mDescriptor = null; if (mIndex != 0) { Assoc nameAndType = (Assoc) pool[mIndex]; mName = (String) pool[nameAndType.a]; mDescriptor = (String) pool[nameAndType.b]; } cd.enclosingMethod(cName, mName, mDescriptor); } } /** *
	 * InnerClasses_attribute {
	 * 	u2 attribute_name_index; 
	 * 	u4 attribute_length; 
	 * 	u2 number_of_classes; {	
	 * 		u2 inner_class_info_index;
	 * 		u2 outer_class_info_index; 
	 * 		u2 inner_name_index; 
	 * 		u2 inner_class_access_flags;
	 * 	} classes[number_of_classes];
	 * }
	 * 
* * @param in * @throws IOException */ private void doInnerClasses(DataInputStream in) throws IOException { int number_of_classes = in.readShort(); for (int i = 0; i < number_of_classes; i++) { int inner_class_info_index = in.readShort(); int outer_class_info_index = in.readShort(); int inner_name_index = in.readShort(); int inner_class_access_flags = in.readShort() & 0xFFFF; if (cd != null) { String innerClass = null; String outerClass = null; String innerName = null; if (inner_class_info_index != 0) { int nameIndex = intPool[inner_class_info_index]; innerClass = (String) pool[nameIndex]; } if (outer_class_info_index != 0) { int nameIndex = intPool[outer_class_info_index]; outerClass = (String) pool[nameIndex]; } if (inner_name_index != 0) innerName = (String) pool[inner_name_index]; cd.innerClass(innerClass, outerClass, innerName, inner_class_access_flags); } } } /** * Handle a signature * *
	 * Signature_attribute { 
	 *     u2 attribute_name_index; 
	 *     u4 attribute_length; 
	 *     u2 signature_index; 
	 *     }
	 * 
* * @param member */ void doSignature(DataInputStream in, ElementType member) throws IOException { int signature_index = in.readUnsignedShort(); String signature = (String) pool[signature_index]; // System.out.println("Signature " + signature ); // The type signature is kind of weird, // lets skip it for now. Seems to be some kind of // type variable name index but it does not seem to // conform to the language specification. if (member != ElementType.TYPE) parseDescriptor(signature); if (last != null) last.signature = signature; if (cd != null) cd.signature(signature); } /** * Handle a constant value call the data collector with it */ void doConstantValue(DataInputStream in) throws IOException { int constantValue_index = in.readUnsignedShort(); if (cd == null) return; Object object = pool[constantValue_index]; if (object == null) object = pool[intPool[constantValue_index]]; last.constant = object; cd.constant(object); } /** *
	 * 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];
	 * 	}
	 * 
* * @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, ElementType.METHOD, 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.invokespecial: { int mref = 0xFFFF & bb.getShort(); if (cd != null) cd.reference(getMethodDef(0, mref)); break; } case OpCodes.invokevirtual: { int mref = 0xFFFF & bb.getShort(); if (cd != null) cd.reference(getMethodDef(0, mref)); break; } case OpCodes.invokeinterface: { int mref = 0xFFFF & bb.getShort(); if (cd != null) cd.reference(getMethodDef(0, mref)); break; } case OpCodes.invokestatic: { int methodref = 0xFFFF & bb.getShort(); if (cd != null) cd.reference(getMethodDef(0, methodref)); if ((methodref == forName || methodref == class$) && lastReference != -1 && pool[intPool[lastReference]] instanceof String) { String clazz = (String) pool[intPool[lastReference]]; if (clazz.startsWith("[") || clazz.endsWith(";")) parseReference(clazz, 0); else { int n = clazz.lastIndexOf('.'); if (n > 0) packageReference(clazz.substring(0, n)); } } 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(); try { bb.position(bb.position() + (high - low + 1) * 4); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 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, ElementType member, RetentionPolicy policy) throws IOException { int num_parameters = in.readUnsignedByte(); for (int p = 0; p < num_parameters; p++) { if (cd != null) cd.parameter(p); doAnnotations(in, member, policy); } } private void doAnnotations(DataInputStream in, ElementType member, RetentionPolicy policy) throws IOException { int num_annotations = in.readUnsignedShort(); // # of annotations for (int a = 0; a < num_annotations; a++) { if (cd == null) doAnnotation(in, member, policy, false); else { Annotation annotion = doAnnotation(in, member, policy, true); cd.annotation(annotion); } } } private Annotation doAnnotation(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect) throws IOException { int type_index = in.readUnsignedShort(); if (annotations == null) annotations = new HashSet(); annotations.add(pool[type_index].toString()); if (policy == RetentionPolicy.RUNTIME) { descriptors.add(new Integer(type_index)); hasRuntimeAnnotations = true; } else { hasClassAnnotations = true; } String name = (String) pool[type_index]; int num_element_value_pairs = in.readUnsignedShort(); Map elements = null; for (int v = 0; v < num_element_value_pairs; v++) { int element_name_index = in.readUnsignedShort(); String element = (String) pool[element_name_index]; Object value = doElementValue(in, member, policy, collect); if (collect) { if (elements == null) elements = new LinkedHashMap(); elements.put(element, value); } } if (collect) return new Annotation(name, elements, member, policy); else return null; } private Object doElementValue(DataInputStream in, ElementType member, RetentionPolicy policy, boolean collect) throws IOException { char tag = (char) in.readUnsignedByte(); switch (tag) { case 'B': // Byte case 'C': // Character case 'I': // Integer case 'S': // Short int const_value_index = in.readUnsignedShort(); return intPool[const_value_index]; case 'D': // Double case 'F': // Float case 's': // String case 'J': // Long const_value_index = in.readUnsignedShort(); return pool[const_value_index]; case 'Z': // Boolean const_value_index = in.readUnsignedShort(); return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true; case 'e': // enum constant int type_name_index = in.readUnsignedShort(); if (policy == RetentionPolicy.RUNTIME) descriptors.add(new Integer(type_name_index)); int const_name_index = in.readUnsignedShort(); return pool[const_name_index]; case 'c': // Class int class_info_index = in.readUnsignedShort(); if (policy == RetentionPolicy.RUNTIME) descriptors.add(new Integer(class_info_index)); return pool[class_info_index]; case '@': // Annotation type return doAnnotation(in, member, policy, collect); case '[': // Array int num_values = in.readUnsignedShort(); Object[] result = new Object[num_values]; for (int i = 0; i < num_values; i++) { result[i] = doElementValue(in, member, policy, collect); } return result; default: throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag); } } /** * Add a new package reference. * * @param pack * A '.' delimited package name */ void packageReference(String pack) { imports.add(pack); } /** * This method parses a descriptor and adds the package of the descriptor to * the referenced packages. * * The syntax of the descriptor is: * *
	 *   descriptor ::= ( '(' reference * ')' )? reference
	 *   reference  ::= 'L' classname ( '<' references '>' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '['
	 * 
* * This methods uses heavy recursion to parse the descriptor and a roving * pointer to limit the creation of string objects. * * @param descriptor * The to be parsed descriptor * @param rover * The pointer to start at */ public void parseDescriptor(String descriptor) { // Some descriptors are weird, they start with a generic // declaration that contains ':', not sure what they mean ... if (descriptor.charAt(0) == '<') return; int rover = 0; if (descriptor.charAt(rover) == '(') { rover = parseReferences(descriptor, rover + 1, ')'); rover++; } parseReferences(descriptor, rover, (char) 0); } /** * Parse a sequence of references. A sequence ends with a given character or * when the string ends. * * @param descriptor * The whole descriptor. * @param rover * The index in the descriptor * @param delimiter * The end character or 0 * @return the last index processed, one character after the delimeter */ int parseReferences(String descriptor, int rover, char delimiter) { while (rover < descriptor.length() && descriptor.charAt(rover) != delimiter) { rover = parseReference(descriptor, rover); } return rover; } /** * Parse a single reference. This can be a single character or an object * reference when it starts with 'L'. * * @param descriptor * The descriptor * @param rover * The place to start * @return The return index after the reference */ int parseReference(String descriptor, int rover) { char c = descriptor.charAt(rover); while (c == '[') c = descriptor.charAt(++rover); if (c == '<') { rover = parseReferences(descriptor, rover + 1, '>'); } else if (c == 'T') { // Type variable name rover++; while (descriptor.charAt(rover) != ';') rover++; } else if (c == 'L') { StringBuilder sb = new StringBuilder(); rover++; int lastSlash = -1; while ((c = descriptor.charAt(rover)) != ';') { if (c == '<') { rover = parseReferences(descriptor, rover + 1, '>'); } else if (c == '/') { lastSlash = sb.length(); sb.append('.'); } else sb.append(c); rover++; } if (cd != null) cd.addReference(sb.toString()); if (lastSlash > 0) packageReference(sb.substring(0, lastSlash)); } else { if ("+-*BCDFIJSZV".indexOf(c) < 0) ;// System.out.println("Should not skip: " + c); } // this skips a lot of characters // [, *, +, -, B, etc. return rover + 1; } public static String getPackage(String clazz) { int n = clazz.lastIndexOf('/'); if (n < 0) { n = clazz.lastIndexOf('.'); if (n < 0) return "."; } return clazz.substring(0, n).replace('/', '.'); } public Set getReferred() { return imports; } String getClassName() { if (className == null) return "NOCLASSNAME"; return className; } public String getPath() { return path; } 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, Analyzer analyzer) throws Exception { 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 PUBLIC: return !isPublic; case CONCRETE: return !isAbstract; case ANNOTATION: if (annotations == null) return false; if (annotations.contains(instr.getPattern())) return true; for (String annotation : annotations) { if (instr.matches(annotation)) return !instr.isNegated(); } return false; case RUNTIMEANNOTATIONS: return hasClassAnnotations; case CLASSANNOTATIONS: return hasClassAnnotations; case ABSTRACT: return isAbstract; case IMPORTS: for (String imp : imports) { if (instr.matches(imp.replace('.', '/'))) return !instr.isNegated(); } } if (zuper == null) return false; Clazz clazz = analyzer.findClass(zuper + ".class"); if (clazz == null) return false; return clazz.is(query, instr, analyzer); } public String toString() { return getFQN(); } public String getFQN() { String s = getClassName().replace('/', '.'); return s; } /** * Return a list of packages implemented by this class. * * @param implemented * @param classspace * @param clazz * @throws Exception */ @SuppressWarnings("deprecation") final static String USEPOLICY = toDescriptor(UsePolicy.class); final static String PROVIDERPOLICY = toDescriptor(ProviderType.class); public static void getImplementedPackages(Set implemented, Analyzer analyzer, Clazz clazz) throws Exception { if (clazz.interfaces != null) { for (String interf : clazz.interfaces) { interf = interf + ".class"; Clazz c = analyzer.getClassspace().get(interf); // If not found, actually parse the imported // class file to check for implementation policy. if (c == null) c = analyzer.findClass(interf); if (c != null) { boolean consumer = false; Set annotations = c.annotations; if (annotations != null) // Override if we marked the interface as a consumer // interface consumer = annotations.contains(USEPOLICY) || annotations.contains(PROVIDERPOLICY); if (!consumer) implemented.add(getPackage(interf)); getImplementedPackages(implemented, analyzer, c); } else implemented.add(getPackage(interf)); } } if (clazz.zuper != null) { Clazz c = analyzer.getClassspace().get(clazz.zuper); if (c != null) { getImplementedPackages(implemented, analyzer, c); } } } // String RNAME = "LaQute/bnd/annotation/UsePolicy;"; public static String toDescriptor(Class clazz) { StringBuilder sb = new StringBuilder(); sb.append('L'); sb.append(clazz.getName().replace('.', '/')); sb.append(';'); return sb.toString(); } MethodDef getMethodDef(int access, int methodRefPoolIndex) { Object o = pool[methodRefPoolIndex]; if (o != null && o instanceof Assoc) { Assoc assoc = (Assoc) o; if (assoc.tag == 10) { int string_index = intPool[assoc.a]; String className = (String) pool[string_index]; int name_and_type_index = assoc.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; String method = (String) pool[name_index]; String descriptor = (String) pool[type_index]; return new MethodDef(access, className, method, descriptor); } else throw new IllegalArgumentException( "Invalid class file (or parsing is wrong), assoc is not type + name (12)"); } else throw new IllegalArgumentException( "Invalid class file (or parsing is wrong), Assoc is not method ref! (10)"); } else throw new IllegalArgumentException( "Invalid class file (or parsing is wrong), Not an assoc at a method ref"); } public static String getShortName(String cname) { int n = cname.lastIndexOf('.'); if (n < 0) return cname; return cname.substring(n + 1, cname.length()); } public static String fqnToPath(String dotted) { return dotted.replace('.', '/') + ".class"; } public static String fqnToBinary(String dotted) { return "L" + dotted.replace('.', '/') + ";"; } public static String pathToFqn(String path) { return path.replace('/', '.').substring(0, path.length() - 6); } public boolean isPublic() { return isPublic; } public boolean isEnum() { return isEnum; } public JAVA getFormat() { return JAVA.format(major); } public static String objectDescriptorToFQN(String string) { if (string.startsWith("L") && string.endsWith(";")) return string.substring(1, string.length() - 1).replace('/', '.'); switch (string.charAt(0)) { case 'V': return "void"; case 'B': return "byte"; case 'C': return "char"; case 'I': return "int"; case 'S': return "short"; case 'D': return "double"; case 'F': return "float"; case 'J': return "long"; case 'Z': return "boolean"; case '[': // Array return objectDescriptorToFQN(string.substring(1)) + "[]"; } throw new IllegalArgumentException("Invalid type character in descriptor " + string); } public static String internalToFqn(String string) { return string.replace('/', '.'); } public static String unCamel(String id) { StringBuilder out = new StringBuilder(); for (int i = 0; i < id.length(); i++) { char c = id.charAt(i); if (c == '_' || c == '$' || c == '.') { if (out.length() > 0 && !Character.isWhitespace(out.charAt(out.length() - 1))) out.append(' '); continue; } int n = i; while (n < id.length() && Character.isUpperCase(id.charAt(n))) { n++; } if (n == i) out.append(id.charAt(i)); else { boolean tolower = (n - i) == 1; if (i > 0 && !Character.isWhitespace(out.charAt(out.length() - 1))) out.append(' '); for (; i < n;) { if (tolower) out.append(Character.toLowerCase(id.charAt(i))); else out.append(id.charAt(i)); i++; } i--; } } if (id.startsWith(".")) out.append(" *"); out.replace(0, 1, Character.toUpperCase(out.charAt(0)) + ""); return out.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy