Search in sources :

Example 1 with ClassDataCollector

use of aQute.bnd.osgi.ClassDataCollector in project bnd by bndtools.

the class ClazzTest method testDynamicInstr.

/**
	 * Complaint from Groovy that the dynamic instruction fails.
	 * 
	 * <pre>
	 *  [bndwrap]
	 * java.lang.ArrayIndexOutOfBoundsException: 15 [bndwrap] at
	 * aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:387) [bndwrap] at
	 * aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:308) [bndwrap] at
	 * aQute.bnd.osgi.Clazz.parseClassFileWithCollector(Clazz.java:297)
	 * [bndwrap] at aQute.bnd.osgi.Clazz.parseClassFile(Clazz.java:286)
	 * [bndwrap] at aQute.bnd.osgi.Analyzer.analyzeJar(Analyzer.java:1489)
	 * [bndwrap] at
	 * aQute.bnd.osgi.Analyzer.analyzeBundleClasspath(Analyzer.java:1387)
	 * [bndwrap] Invalid class file:
	 * groovy/inspect/swingui/AstNodeToScriptVisitor.class [bndwrap] Exception:
	 * 15
	 * </pre>
	 */
public void testDynamicInstr() throws Exception {
    try (Analyzer a = new Analyzer()) {
        Clazz c = new Clazz(a, "", null);
        c.parseClassFile(new FileInputStream("jar/AstNodeToScriptVisitor.jclass"), new ClassDataCollector() {
        });
        Set<PackageRef> referred = c.getReferred();
        Descriptors d = new Descriptors();
        assertFalse(referred.contains(d.getPackageRef("")));
        System.out.println(referred);
    }
}
Also used : Clazz(aQute.bnd.osgi.Clazz) Descriptors(aQute.bnd.osgi.Descriptors) Analyzer(aQute.bnd.osgi.Analyzer) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) PackageRef(aQute.bnd.osgi.Descriptors.PackageRef) FileInputStream(java.io.FileInputStream)

Example 2 with ClassDataCollector

use of aQute.bnd.osgi.ClassDataCollector in project bnd by bndtools.

the class ClazzTest method testClassForNameFalsePickup.

/**
	 * Check if the class is not picking up false references when the
	 * CLass.forName name is constructed. The DeploymentAdminPermission.1.jclass
	 * turned out to use Class.forName with a name that was prefixed with a
	 * package from a property. bnd discovered the suffix
	 * (.DeploymentAdminPermission) but this ended up in the default package. So
	 * now the clazz parser tests that the name guessed for Class.forName must
	 * actually resemble a class name.
	 */
public void testClassForNameFalsePickup() throws Exception {
    try (Analyzer a = new Analyzer()) {
        Clazz c = new Clazz(a, "", null);
        c.parseClassFile(new FileInputStream("jar/DeploymentAdminPermission.1.jclass"), new ClassDataCollector() {
        });
        Set<PackageRef> referred = c.getReferred();
        Descriptors d = new Descriptors();
        assertFalse(referred.contains(d.getPackageRef("")));
        System.out.println(referred);
    }
}
Also used : Clazz(aQute.bnd.osgi.Clazz) Descriptors(aQute.bnd.osgi.Descriptors) Analyzer(aQute.bnd.osgi.Analyzer) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) PackageRef(aQute.bnd.osgi.Descriptors.PackageRef) FileInputStream(java.io.FileInputStream)

Example 3 with ClassDataCollector

use of aQute.bnd.osgi.ClassDataCollector in project bnd by bndtools.

the class ClazzTest method testCaughtExceptions.

public void testCaughtExceptions() throws Exception {
    try (Analyzer a = new Analyzer()) {
        Clazz c = new Clazz(a, "", null);
        c.parseClassFile(new FileInputStream("bin/test/ClazzTest$Catching.class"), new ClassDataCollector() {
        });
        assertTrue(c.getReferred().toString().contains("org.xml.sax"));
    }
}
Also used : Clazz(aQute.bnd.osgi.Clazz) Analyzer(aQute.bnd.osgi.Analyzer) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) FileInputStream(java.io.FileInputStream)

Example 4 with ClassDataCollector

use of aQute.bnd.osgi.ClassDataCollector in project bnd by bndtools.

the class ClassParserTest method testConstantValues.

/**
	 * Test the constant values
	 * 
	 * @throws Exception
	 */
public void testConstantValues() throws Exception {
    final Map<String, Object> values = new HashMap<String, Object>();
    Clazz c = new Clazz(a, "ConstantValues", new FileResource(IO.getFile(new File("").getAbsoluteFile(), "bin/test/ConstantValues.class")));
    c.parseClassFileWithCollector(new ClassDataCollector() {

        Clazz.FieldDef last;

        @Override
        public void field(Clazz.FieldDef referenced) {
            last = referenced;
        }

        @Override
        public void constant(Object value) {
            values.put(last.getName(), value);
        }
    });
    assertEquals(1, values.get("t"));
    assertEquals(0, values.get("f"));
    assertEquals((int) Byte.MAX_VALUE, values.get("bt"));
    assertEquals((int) Short.MAX_VALUE, values.get("shrt"));
    assertEquals((int) Character.MAX_VALUE, values.get("chr"));
    assertEquals(Integer.MAX_VALUE, values.get("intgr"));
    assertEquals(Long.MAX_VALUE, values.get("lng"));
    assertEquals(Float.MAX_VALUE, values.get("flt"));
    assertEquals(Double.MAX_VALUE, values.get("dbl"));
    assertEquals("blabla", values.get("strng"));
// Classes are special
// assertEquals("java.lang.Object", ((Clazz.ClassConstant)
// values.get("clss")).getName());
}
Also used : HashMap(java.util.HashMap) FileResource(aQute.bnd.osgi.FileResource) Clazz(aQute.bnd.osgi.Clazz) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) File(java.io.File)

Example 5 with ClassDataCollector

use of aQute.bnd.osgi.ClassDataCollector in project bnd by bndtools.

the class JavaElement method classElement.

/**
	 * Calculate the class element. This requires parsing the class file and
	 * finding all the methods that were added etc. The parsing will take super
	 * interfaces and super classes into account. For this reason it maintains a
	 * queue of classes/interfaces to parse.
	 * 
	 * @param analyzer
	 * @param clazz
	 * @param infos
	 * @throws Exception
	 */
Element classElement(final Clazz clazz) throws Exception {
    Element e = cache.get(clazz);
    if (e != null)
        return e;
    final Set<Element> members = Create.set();
    final Set<MethodDef> methods = Create.set();
    final Set<Clazz.FieldDef> fields = Create.set();
    final MultiMap<Clazz.Def, Element> annotations = new MultiMap<Clazz.Def, Element>();
    final TypeRef name = clazz.getClassName();
    final String fqn = name.getFQN();
    final String shortName = name.getShortName();
    // Check if this clazz is actually a provider or not
    // providers must be listed in the exported package in the
    // PROVIDER_TYPE directive.
    Instructions matchers = providerMatcher.get(name.getPackageRef());
    boolean p = matchers != null && matchers.matches(shortName);
    final AtomicBoolean provider = new AtomicBoolean(p);
    //
    // Check if we already had this clazz in the cache
    //
    // for super classes
    Element before = cache.get(clazz);
    if (before != null)
        return before;
    clazz.parseClassFileWithCollector(new ClassDataCollector() {

        boolean memberEnd;

        Clazz.FieldDef last;

        @Override
        public void version(int minor, int major) {
            javas.add(Clazz.JAVA.getJava(major, minor));
        }

        @Override
        public void method(MethodDef defined) {
            if ((defined.isProtected() || defined.isPublic())) {
                last = defined;
                methods.add(defined);
            } else {
                last = null;
            }
        }

        @Override
        public void deprecated() {
            if (memberEnd)
                clazz.setDeprecated(true);
            else if (last != null)
                last.setDeprecated(true);
        }

        @Override
        public void field(Clazz.FieldDef defined) {
            if (defined.isProtected() || defined.isPublic()) {
                last = defined;
                fields.add(defined);
            } else
                last = null;
        }

        @Override
        public void constant(Object o) {
            if (last != null) {
                // Must be accessible now
                last.setConstant(o);
            }
        }

        @Override
        public void extendsClass(TypeRef name) throws Exception {
            String comment = null;
            if (!clazz.isInterface())
                comment = inherit(members, name);
            Clazz c = analyzer.findClass(name);
            if ((c == null || c.isPublic()) && !name.isObject())
                members.add(new Element(EXTENDS, name.getFQN(), null, MICRO, MAJOR, comment));
        }

        @Override
        public void implementsInterfaces(TypeRef[] names) throws Exception {
            // ignore type reordering
            Arrays.sort(names);
            for (TypeRef name : names) {
                String comment = null;
                if (clazz.isInterface() || clazz.isAbstract())
                    comment = inherit(members, name);
                members.add(new Element(IMPLEMENTS, name.getFQN(), null, MINOR, MAJOR, comment));
            }
        }

        /**
			 */
        Set<Element> OBJECT = Create.set();

        public String inherit(final Set<Element> members, TypeRef name) throws Exception {
            if (name.isObject()) {
                if (OBJECT.isEmpty()) {
                    Clazz c = analyzer.findClass(name);
                    if (c == null) {
                        // available
                        return null;
                    }
                    Element s = classElement(c);
                    for (Element child : s.children) {
                        if (INHERITED.contains(child.type)) {
                            String n = child.getName();
                            if (child.type == METHOD) {
                                if (n.startsWith("<init>") || "getClass()".equals(child.getName()) || n.startsWith("wait(") || n.startsWith("notify(") || n.startsWith("notifyAll("))
                                    continue;
                            }
                            if (isStatic(child))
                                continue;
                            OBJECT.add(child);
                        }
                    }
                }
                members.addAll(OBJECT);
            } else {
                Clazz c = analyzer.findClass(name);
                if (c == null) {
                    return inherit(members, analyzer.getTypeRef("java/lang/Object"));
                }
                Element s = classElement(c);
                for (Element child : s.children) {
                    if (isStatic(child))
                        continue;
                    if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
                        members.add(child);
                    }
                }
            }
            return null;
        }

        private boolean isStatic(Element child) {
            boolean isStatic = child.get("static") != null;
            return isStatic;
        }

        /**
			 * Deprecated annotations and Provider/Consumer Type (both bnd and
			 * OSGi) are treated special. Other annotations are turned into a
			 * tree. Starting with ANNOTATED, and then properties. A property is
			 * a PROPERTY property or an ANNOTATED property if it is an
			 * annotation. If it is an array, the key is suffixed with the
			 * index.
			 * 
			 * <pre>
			 *  public @interface Outer { Inner[] value(); }
			 * public @interface Inner { String[] value(); } @Outer(
			 * { @Inner("1","2"}) } class Xyz {} ANNOTATED Outer
			 * (CHANGED/CHANGED) ANNOTATED Inner (CHANGED/CHANGED) PROPERTY
			 * value.0=1 (CHANGED/CHANGED) PROPERTY value.1=2 (CHANGED/CHANGED)
			 * </pre>
			 */
        @Override
        public void annotation(Annotation annotation) {
            if (Deprecated.class.getName().equals(annotation.getName().getFQN())) {
                if (memberEnd)
                    clazz.setDeprecated(true);
                else if (last != null)
                    last.setDeprecated(true);
                return;
            }
            Element e = annotatedToElement(annotation);
            if (memberEnd) {
                members.add(e);
                //
                // Check for the provider/consumer. We use strings because
                // these are not officially
                // released yet
                //
                String name = annotation.getName().getFQN();
                if ("aQute.bnd.annotation.ProviderType".equals(name) || "org.osgi.annotation.versioning.ProviderType".equals(name)) {
                    provider.set(true);
                } else if ("aQute.bnd.annotation.ConsumerType".equals(name) || "org.osgi.annotation.versioning.ConsumerType".equals(name)) {
                    provider.set(false);
                }
            } else if (last != null)
                annotations.add(last, e);
        }

        /*
			 * Return an ANNOTATED element for this annotation. An ANNOTATED
			 * element contains either PROPERTY children or ANNOTATED children.
			 */
        private Element annotatedToElement(Annotation annotation) {
            Collection<Element> properties = Create.set();
            for (String key : annotation.keySet()) {
                addAnnotationMember(properties, key, annotation.get(key));
            }
            return new Element(ANNOTATED, annotation.getName().getFQN(), properties, CHANGED, CHANGED, null);
        }

        /*
			 * This method detects 3 cases: An Annotation, which means it
			 * creates a new child ANNOTATED element, an array, which means it
			 * will repeat recursively but suffixes the key with the index, or a
			 * simple value which is turned into a string.
			 */
        private void addAnnotationMember(Collection<Element> properties, String key, Object member) {
            if (member instanceof Annotation) {
                properties.add(annotatedToElement((Annotation) member));
            } else if (member.getClass().isArray()) {
                int l = Array.getLength(member);
                for (int i = 0; i < l; i++) {
                    addAnnotationMember(properties, key + "." + i, Array.get(member, i));
                }
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append(key);
                sb.append('=');
                if (member instanceof String) {
                    sb.append("'");
                    sb.append(member);
                    sb.append("'");
                } else
                    sb.append(member);
                properties.add(new Element(PROPERTY, sb.toString(), null, CHANGED, CHANGED, null));
            }
        }

        @Override
        public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, int innerClassAccessFlags) throws Exception {
            Clazz clazz = analyzer.findClass(innerClass);
            if (clazz != null)
                clazz.setInnerAccess(innerClassAccessFlags);
            if (Modifier.isProtected(innerClassAccessFlags) || Modifier.isPublic(innerClassAccessFlags))
                return;
            notAccessible.add(innerClass);
        }

        @Override
        public void memberEnd() {
            memberEnd = true;
        }
    });
    // This is the heart of the semantic versioning. If we
    // add or remove a method from an interface then
    Delta add;
    Delta remove;
    Type type;
    if (clazz.isInterface())
        if (clazz.isAnnotation())
            type = ANNOTATION;
        else
            type = INTERFACE;
    else if (clazz.isEnum())
        type = ENUM;
    else
        type = CLASS;
    if (type == INTERFACE) {
        if (provider.get()) {
            // Adding a method for a provider is not an issue
            // because it must be aware of the changes
            add = MINOR;
            // Removing a method influences consumers since they
            // tend to call this guy.
            remove = MAJOR;
        } else {
            // Adding a method is a major change
            // because the consumer has to implement it
            // or the provider will call a non existent
            // method on the consumer
            add = MAJOR;
            // Removing a method is not an issue for
            // providers, however, consumers could potentially
            // call through this interface :-(
            remove = MAJOR;
        }
    } else {
        // Adding a method to a class can never do any harm
        // except when the class is extended and the new
        // method clashes with the new method. That is
        // why API classes in general should be final, at
        // least not extended by consumers.
        add = MINOR;
        // Removing it will likely hurt consumers
        remove = MAJOR;
    }
    for (MethodDef m : methods) {
        if (m.isSynthetic()) {
            // Ignore synthetic methods
            continue;
        }
        Collection<Element> children = annotations.get(m);
        if (children == null)
            children = new HashSet<Element>();
        access(children, m.getAccess(), m.isDeprecated(), provider.get());
        // in a final class.
        if (clazz.isFinal())
            children.remove(FINAL);
        children.add(getReturn(m.getType()));
        if (clazz.isInterface() && !m.isAbstract()) {
            //
            // We have a Java 8 default method!
            // Such a method is always a minor update
            //
            add = MINOR;
        }
        String signature = m.getName() + toString(m.getPrototype());
        Element member = new Element(METHOD, signature, children, add, provider.get() && !m.isPublic() ? MINOR : remove, null);
        if (!members.add(member)) {
            members.remove(member);
            members.add(member);
        }
    }
    for (Clazz.FieldDef f : fields) {
        if (f.isSynthetic()) {
            // Ignore synthetic fields
            continue;
        }
        Collection<Element> children = annotations.get(f);
        if (children == null)
            children = new HashSet<Element>();
        // Fields can have a constant value, this is a new element
        if (f.getConstant() != null) {
            children.add(new Element(CONSTANT, f.getConstant().toString(), null, CHANGED, CHANGED, null));
        }
        access(children, f.getAccess(), f.isDeprecated(), provider.get());
        children.add(getReturn(f.getType()));
        Element member = new Element(FIELD, f.getName(), children, MINOR, provider.get() && !f.isPublic() ? MINOR : MAJOR, null);
        if (!members.add(member)) {
            members.remove(member);
            members.add(member);
        }
    }
    access(members, clazz.getAccess(), clazz.isDeprecated(), provider.get());
    // And make the result
    Element s = new Element(type, fqn, members, MINOR, MAJOR, null);
    cache.put(clazz, s);
    return s;
}
Also used : MethodDef(aQute.bnd.osgi.Clazz.MethodDef) TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) MultiMap(aQute.lib.collections.MultiMap) Clazz(aQute.bnd.osgi.Clazz) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) HashSet(java.util.HashSet) MethodDef(aQute.bnd.osgi.Clazz.MethodDef) Instructions(aQute.bnd.osgi.Instructions) Annotation(aQute.bnd.osgi.Annotation) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Type(aQute.bnd.service.diff.Type) Delta(aQute.bnd.service.diff.Delta) Collection(java.util.Collection)

Aggregations

ClassDataCollector (aQute.bnd.osgi.ClassDataCollector)14 Clazz (aQute.bnd.osgi.Clazz)14 Analyzer (aQute.bnd.osgi.Analyzer)8 FileInputStream (java.io.FileInputStream)6 TypeRef (aQute.bnd.osgi.Descriptors.TypeRef)5 PackageRef (aQute.bnd.osgi.Descriptors.PackageRef)4 MethodDef (aQute.bnd.osgi.Clazz.MethodDef)3 Descriptors (aQute.bnd.osgi.Descriptors)3 Annotation (aQute.bnd.osgi.Annotation)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Set (java.util.Set)2 FileResource (aQute.bnd.osgi.FileResource)1 Instructions (aQute.bnd.osgi.Instructions)1 Delta (aQute.bnd.service.diff.Delta)1 Type (aQute.bnd.service.diff.Type)1 MultiMap (aQute.lib.collections.MultiMap)1 File (java.io.File)1 Collection (java.util.Collection)1 LinkedHashMap (java.util.LinkedHashMap)1