Re: ASM4 / Java 7
| Newsgroups | gmane.comp.java.objectweb.asm |
|---|---|
| Message-ID | <738016199.10268981314708525878.JavaMail.root@zimbra7-e1.priv.proxad.net> |
27/08/2011 00:59, Jeremy Manson wrote: > You guys have a workaround for this, which is basically "copy the code > in http://websvn.ow2.org/filedetails.php?repname=asm&path=%2Ftrunk%2Fasm%2Ftest%2Fconform%2Forg%2Fow2%2Fasm%2FClassWriterComputeFramesTest.java". > But doesn't it seem as if this would be a common enough problem for > the code to be extracted from this test to somewhere everyone can use > it, if not made the default behavior for ClassWriter? Do I have the > wrong end of the stick here? the code in ClassWriterComputeFramesTest is quite inefficient: on my PC this test runs in 16.5 s for 9348 classes. If I remove the overriden "getCommonSuperClass" method (i.e. using the default ClassWriter implementation), it runs in 13.4s. Attached is an experimental improved version (patch for SVN HEAD) of the code in ClassWriterComputeFramesTest, which might be integrated directly in ClassWriter. With this version, the test runs in 14.6s (2 seconds less than the ClassWriterComputeFramesTest code, but still 1 second more than the current ClassWriter code). I'm not sure what to do with this yet... Eric
commonsuperclass.patch
(application/octet-stream, 7.5 KB)
Index: ClassReader.java
===================================================================
--- ClassReader.java (revision 1529)
+++ ClassReader.java (working copy)
@@ -473,7 +473,7 @@
* @return the bytecode read from the given input stream.
* @throws IOException if a problem occurs during reading.
*/
- private static byte[] readClass(final InputStream is, boolean close)
+ static byte[] readClass(final InputStream is, boolean close)
throws IOException
{
if (is == null) {
Index: ClassWriter.java
===================================================================
--- ClassWriter.java (revision 1530)
+++ ClassWriter.java (working copy)
@@ -29,6 +29,8 @@
*/
package org.objectweb.asm;
+import java.io.IOException;
+
/**
* A {@link ClassVisitor} that generates classes in bytecode form. More
* precisely this visitor generates a byte array conforming to the Java class
@@ -1565,12 +1567,15 @@
/**
* Returns the common super type of the two given types. The default
- * implementation of this method <i>loads<i> the two given classes and uses
- * the java.lang.Class methods to find the common super class. It can be
- * overridden to compute this common super type in other ways, in particular
- * without actually loading any class, or to take into account the class
- * that is currently being generated by this ClassWriter, which can of
- * course not be loaded since it is under construction.
+ * implementation of this method loads the <i>bytecode</i> of the two given
+ * classes, of their super classes, and so on recursively (up to Object).
+ * The bytecode of these classes is loaded with the
+ * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream}
+ * method, using the class loader of this ClassWriter. This method can be
+ * overridden to compute this common super type in other ways, for instance
+ * to take into account the class that is currently being generated by this
+ * ClassWriter, which can of course not be loaded since it is under
+ * construction.
*
* @param type1 the internal name of a class.
* @param type2 the internal name of another class.
@@ -1579,31 +1584,124 @@
*/
protected String getCommonSuperClass(final String type1, final String type2)
{
- Class<?> c, d;
- ClassLoader classLoader = getClass().getClassLoader();
try {
- c = Class.forName(type1.replace('/', '.'), false, classLoader);
- d = Class.forName(type2.replace('/', '.'), false, classLoader);
- } catch (Exception e) {
+ ClassReader info1 = typeInfo(type1);
+ ClassReader info2 = typeInfo(type2);
+ if ((info1.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
+ if (typeImplements(type2, info2, type1)) {
+ return type1;
+ } else {
+ return "java/lang/Object";
+ }
+ }
+ if ((info2.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
+ if (typeImplements(type1, info1, type2)) {
+ return type2;
+ } else {
+ return "java/lang/Object";
+ }
+ }
+ StringBuilder b1 = typeAncestors(type1, info1);
+ StringBuilder b2 = typeAncestors(type2, info2);
+ String result = "java/lang/Object";
+ int end1 = b1.length();
+ int end2 = b2.length();
+ while (true) {
+ int start1 = b1.lastIndexOf(";", end1 - 1);
+ int start2 = b2.lastIndexOf(";", end2 - 1);
+ if (start1 != -1 && start2 != -1
+ && end1 - start1 == end1 - start1)
+ {
+ String p1 = b1.substring(start1 + 1, end1);
+ String p2 = b2.substring(start2 + 1, end2);
+ if (p1.equals(p2)) {
+ result = p1;
+ end1 = start1;
+ end2 = start2;
+ } else {
+ return result;
+ }
+ } else {
+ return result;
+ }
+ }
+ } catch (IOException e) {
throw new RuntimeException(e.toString());
}
- if (c.isAssignableFrom(d)) {
- return type1;
+ }
+
+ /**
+ * Returns the internal names of the ancestor classes of the given type.
+ *
+ * @param type the internal name of a class or interface.
+ * @param info the ClassReader corresponding to 'type'.
+ * @return a StringBuilder containing the ancestor classes of 'type',
+ * separated by ';'. The returned string has the following format:
+ * ";type1;type2 ... ;typeN", where type1 is 'type', and typeN is a
+ * direct subclass of Object. If 'type' is Object, the returned
+ * string is empty.
+ * @throws IOException if the bytecode of 'type' or of some of its ancestor
+ * class cannot be loaded.
+ */
+ private StringBuilder typeAncestors(String type, ClassReader info)
+ throws IOException
+ {
+ StringBuilder b = new StringBuilder();
+ while (!"java/lang/Object".equals(type)) {
+ b.append(';').append(type);
+ type = info.getSuperName();
+ info = typeInfo(type);
}
- if (d.isAssignableFrom(c)) {
- return type2;
+ return b;
+ }
+
+ /**
+ * Returns true if the given type implements the given interface.
+ *
+ * @param type the internal name of a class or interface.
+ * @param info the ClassReader corresponding to 'type'.
+ * @param itf the internal name of a interface.
+ * @return true if 'type' implements directly or indirectly 'itf'
+ * @throws IOException if the bytecode of 'type' or of some of its ancestor
+ * class cannot be loaded.
+ */
+ private boolean typeImplements(
+ String type,
+ ClassReader info,
+ String itf) throws IOException
+ {
+ while (!"java/lang/Object".equals(type)) {
+ String[] itfs = info.getInterfaces();
+ for (int i = 0; i < itfs.length; ++i) {
+ if (itfs[i].equals(itf)) {
+ return true;
+ }
+ }
+ for (int i = 0; i < itfs.length; ++i) {
+ if (typeImplements(itfs[i], typeInfo(itfs[i]), itf)) {
+ return true;
+ }
+ }
+ type = info.getSuperName();
+ info = typeInfo(type);
}
- if (c.isInterface() || d.isInterface()) {
- return "java/lang/Object";
- } else {
- do {
- c = c.getSuperclass();
- } while (!c.isAssignableFrom(d));
- return c.getName().replace('.', '/');
- }
+ return false;
}
/**
+ * Returns a ClassReader corresponding to the given class or interface.
+ *
+ * @param type the internal name of a class or interface.
+ * @return the ClassReader corresponding to 'type'.
+ * @throws IOException if the bytecode of 'type' cannot be loaded.
+ */
+ private ClassReader typeInfo(final String type) throws IOException {
+ return new ClassReader(ClassReader.readClass(getClass().getClassLoader()
+ .getResourceAsStream(type + ".class"),
+ true));
+ }
+
+ /**
* Returns the constant pool's hash table item which is equal to the given
* item.
*
message-footer.txt
(text/plain, 238 B)
-- You receive this message as a subscriber of the [email protected] mailing list. To unsubscribe: mailto:[email protected] For general help: mailto:[email protected]?subject=help OW2 mailing lists service home page: http://www.ow2.org/wws