Permalink
Browse files

Now we use ASM for bytecode engineering to resolve constants as we want.

Also fixed double precision issue.
  • Loading branch information...
1 parent ec700d3 commit 7b0f30f77667d01e8078c5391c8559fb9feb8bdf @atsushieno atsushieno committed Aug 19, 2011
Showing with 86 additions and 35 deletions.
  1. +5 −1 .gitignore
  2. +12 −2 JavaArchive.java
  3. +41 −14 JavaClass.java
  4. +4 −0 MANIFEST.MF
  5. +3 −3 Makefile
  6. +21 −15 README
  7. BIN asm-debug-all-4.0_RC1.jar
View
@@ -1,2 +1,6 @@
-*.jar
+jar2xml.jar
obj
+tmpout/*.xml
+annotations/*.xml
+scraper.exe
+scraper.exe.mdb
View
@@ -25,6 +25,7 @@
package jar2xml;
import java.io.File;
+import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarFile;
@@ -34,6 +35,8 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
+import org.objectweb.asm.*;
+import org.objectweb.asm.tree.*;
public class JavaArchive {
@@ -61,14 +64,21 @@ public JavaArchive (String filename) throws Exception
if (name.endsWith (".class")) {
name = name.substring (0, name.length () - 6);
try {
+ InputStream stream = file.getInputStream (entry);
+ ClassReader reader = new ClassReader (stream);
+ ClassNode node = new ClassNode ();
+ reader.accept (node, 0);
+
Class c = loader.loadClass (name.replace ('/', '.'));
- String pkgname = c.getPackage ().getName ();
+ //String pkgname = c.getPackage ().getName ();
+ String pkgname = name.substring (0, name.lastIndexOf ('/')).replace ('/', '.');
+
JavaPackage pkg = packages.get (pkgname);
if (pkg == null) {
pkg = new JavaPackage (pkgname);
packages.put (pkgname, pkg);
}
- pkg.addClass (new JavaClass (c));
+ pkg.addClass (new JavaClass (c, node));
} catch (Throwable t) {
System.err.println ("Couldn't load class " + name);
}
View
@@ -42,18 +42,26 @@
import java.util.regex.Matcher;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.objectweb.asm.*;
+import org.objectweb.asm.tree.*;
public class JavaClass implements Comparable<JavaClass> {
private Class jclass;
+ private ClassNode asm;
+ private Map<String,FieldNode> asmFields;
private List<String> deprecatedFields;
private List<String> deprecatedMethods;
- public JavaClass (Class jclass)
+ public JavaClass (Class jclass, ClassNode asm)
{
this.jclass = jclass;
deprecatedFields = AndroidDocScraper.getDeprecatedFields (jclass);
deprecatedMethods = AndroidDocScraper.getDeprecatedMethods (jclass);
+ asmFields = new HashMap<String,FieldNode> ();
+
+ for (FieldNode fn : (List<FieldNode>) asm.fields)
+ asmFields.put (fn.name, fn);
}
public int compareTo (JavaClass jc)
@@ -134,7 +142,7 @@ int getConstructorParameterOffset (Constructor ctor)
return 0;
}
- void appendField (Field field, Document doc, Element parent)
+ void appendField (Field field, FieldNode asmField, Document doc, Element parent)
{
int mods = field.getModifiers ();
if (!Modifier.isPublic (mods) && !Modifier.isProtected (mods))
@@ -153,24 +161,40 @@ void appendField (Field field, Document doc, Element parent)
e.setAttribute ("visibility", Modifier.isPublic (mods) ? "public" : "protected");
e.setAttribute ("volatile", Modifier.isVolatile (mods) ? "true" : "false");
setDeprecatedAttr (e, field.getDeclaredAnnotations (), e.getAttribute ("name"));
- if (Modifier.isStatic (mods) && Modifier.isFinal (mods) && Modifier.isPublic (mods)) {
+
+ // *** constant value retrieval ***
+ // sadly, there is no perfect solution:
+ // - basically we want to use ASM, but sometimes ASM fails
+ // to create FieldNode instance.
+ // - on the other hand, reflection
+ // - does not allow access to protected fields.
+ // - sometimes returns "default" value for "undefined"
+ // values such as 0 for ints and false for boolean.
+ //
+ // basically we use ASM here.
+
+ if (asmField == null)
+ // this happens to couple of fields on java.awt.font.TextAttribute, java.lang.Double/Float and so on.
+ System.err.println ("!!!!! WARNING!!! null ASM FieldNode for " + field);
+ else if (asmField.value != null) {
String type = e.getAttribute ("type");
+ boolean isPublic = Modifier.isPublic (mods);
try {
if (type == "int")
- e.setAttribute ("value", String.format ("%d", field.getInt (null)));
+ e.setAttribute ("value", String.format ("%d", asmField.value));
else if (type == "byte")
- e.setAttribute ("value", String.format ("%d", field.getByte (null)));
+ e.setAttribute ("value", String.format ("%d", asmField.value));
else if (type == "char")
- e.setAttribute ("value", String.format ("%d", (int) field.getChar (null)));
+ e.setAttribute ("value", String.format ("%d", asmField.value));
else if (type == "short")
- e.setAttribute ("value", String.format ("%d", field.getShort (null)));
+ e.setAttribute ("value", String.format ("%d", asmField.value));
else if (type == "long")
- e.setAttribute ("value", String.format ("%dL", field.getLong (null)));
+ e.setAttribute ("value", String.format ("%dL", asmField.value));
else if (type == "float")
- e.setAttribute ("value", String.format ("%f", field.getFloat (null)));
+ e.setAttribute ("value", String.format ("%f", isPublic ? field.getFloat (null) : asmField.value));
else if (type == "double") {
// see java.lang.Double constants.
- double dvalue = field.getDouble (null);
+ double dvalue = (Double) asmField.value;
String svalue;
if (dvalue == Double.MAX_VALUE)
@@ -184,13 +208,16 @@ else if (dvalue == Double.POSITIVE_INFINITY)
else if (dvalue == Double.NEGATIVE_INFINITY)
svalue = "(-1.0 / 0.0)";
else
- svalue = String.format ("%f", dvalue);
+ // FIXME: here we specify "limited" digits for formatting.
+ // This should fix most cases, but this could still result in not-precise value.
+ // Math.E and Math.PI works with this.
+ svalue = String.format ("%.15f", dvalue);
e.setAttribute ("value", svalue);
}
else if (type == "boolean")
- e.setAttribute ("value", field.getBoolean (null) ? "true" : "false");
+ e.setAttribute ("value", 0 == (Integer) asmField.value ? "true" : "false");
else if (type == "java.lang.String") {
- String value = (String) field.get (null);
+ String value = (String) asmField.value;
if (value != null)
e.setAttribute ("value", "\"" + value.replace ("\\", "\\\\") + "\"");
}
@@ -467,7 +494,7 @@ else if (hret != null && !mret.isAssignableFrom (hret)) {
Field [] fields = jclass.getDeclaredFields ();
sortFields (fields);
for (Field field : fields)
- appendField (field, doc, e);
+ appendField (field, asmFields.get (field.getName ()), doc, e);
}
parent.appendChild (e);
}
View
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Main-Class: jar2xml.Start
+Class-Path: asm-debug-all-3.3.1.jar
+
View
@@ -17,11 +17,11 @@ sources = \
JavaPackage.java \
Start.java
-$(TARGET): $(sources)
+$(TARGET): $(sources) MANIFEST.MF
-rm -rf obj
mkdir -p obj
- javac -g -d obj $(sources)
- jar cfe "$@" jar2xml.Start -C obj/ .
+ javac -g -d obj $(sources) -cp asm-debug-all-4.0_RC1.jar
+ jar cfm "$@" MANIFEST.MF asm-debug-all-4.0_RC1.jar -C obj/ .
scraper.exe : scraper.cs
mcs -debug scraper.cs
View
36 README
@@ -8,6 +8,7 @@ and associated documentation. The information is stored in XML format.
- xmllint (for doc scraping)
- xmlstarlet (ditto)
+ Also it uses ASM (included in the sources)
* Tools
@@ -77,21 +78,6 @@ and associated documentation. The information is stored in XML format.
excluded, but this brings another check to *not* exclude methods when
the corresponding base methods are excluded).
-** Byte code engineering
-
-- Some fields seem to be incorrectly marked as constant. For example,
- android.os.Build.TIME is not a constant, but since the field.getLong()
- returns 0, it is set as 0 (and it does not return null for get()).
-
- java.io.File.pathSeparatorChar has the same problem, but it is worse;
- it returns non-zero value so we cannot depend on the value.
-
- Solution ideas: scrape constant values, or use ASM.
-
-- Constant values for "protected" fields need to be retrieved, but Java reflection API throws IllegalAccessException for such attempt.
-
- This likely has to be resolved by bytecode engineering (such as ASM).
-
** Method override resolution
- Do not skip certain overriden methods. This in JavaClass.java gives
@@ -233,3 +219,23 @@ Now we have version-aware scraper and resolved the issue described below.
A possible approach is to checkout API docs for *every* version (from
AOSP).
</historical>
+
+
+** Byte code engineering
+
+Now we use ASM to solve the issues below:
+
+<historical>
+- Some fields were incorrectly marked as constant. For example,
+ android.os.Build.TIME is not a constant, but since the field.getLong()
+ returns 0, it is set as 0 (and it does not return null for get()).
+
+ java.io.File.pathSeparatorChar has the same problem, but it is worse;
+ it returns non-zero value so we cannot depend on the value.
+
+ Solution ideas: scrape constant values, or use ASM.
+
+- Constant values for "protected" fields need to be retrieved, but Java reflection API throws IllegalAccessException for such attempt.
+
+ This likely has to be resolved by bytecode engineering (such as ASM).
+</historical>
View
Binary file not shown.

0 comments on commit 7b0f30f

Please sign in to comment.