changeset 124:8bb44682be11

Implemented lazy parsing for DEX parser. * dex/ClassData.java (getCode): Perform lazy parsing. * dex/ClassDef.java (getClassData, getStaticValues): Likewise. * dex/Code.java (getDebugInfo): Likewise.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Sun, 10 Apr 2011 23:54:27 +0200
parents 3ec98ede8a06
children 0355dd717a24
files src/main/java/org/icedrobot/daneel/dex/ClassData.java src/main/java/org/icedrobot/daneel/dex/ClassDef.java src/main/java/org/icedrobot/daneel/dex/Code.java
diffstat 3 files changed, 121 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Sun Apr 10 23:52:10 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Sun Apr 10 23:54:27 2011 +0200
@@ -61,6 +61,8 @@
         return new ClassData(buffer, dex, classDef);
     }
 
+    private final DexFile dex;
+
     private final ClassDef classDef;
 
     private int staticFieldsSize;
@@ -79,15 +81,14 @@
 
     private final MethodId[] directMethodsIds;
     private int[] directMethodsFlags;
-    private final Code[] directMethodsCode;
+    private final int[] directMethodsCodeOff;
 
     private final MethodId[] virtualMethodsIds;
     private int[] virtualMethodsFlags;
-    private final Code[] virtualMethodsCode;
-
-    private final Object[] staticValues;
+    private final int[] virtualMethodsCodeOff;
 
     private ClassData(ByteBuffer buffer, DexFile dex, ClassDef classDef) {
+        this.dex = dex;
         this.classDef = classDef;
         staticFieldsSize = BufferUtil.getULEB128(buffer);
         instanceFieldsSize = BufferUtil.getULEB128(buffer);
@@ -119,84 +120,47 @@
         // Parse encoded_method structures in direct_methods array.
         directMethodsIds = new MethodId[directMethodsSize];
         directMethodsFlags = new int[directMethodsSize];
-        directMethodsCode = new Code[directMethodsSize];
+        directMethodsCodeOff = new int[directMethodsSize];
         int directMethodsIdx = 0;
         for (int i = 0; i < directMethodsSize; i++) {
             directMethodsIdx += BufferUtil.getULEB128(buffer);
-            MethodId method = dex.getMethodId(directMethodsIdx);
-            int flags = BufferUtil.getULEB128(buffer);
-            int codeOff = BufferUtil.getULEB128(buffer);
-            directMethodsIds[i] = method;
-            directMethodsFlags[i] = flags;
-            if (codeOff != 0)
-                directMethodsCode[i] = Code.parse(dex.getDataBuffer(codeOff),
-                        dex, method, flags);
+            directMethodsIds[i] = dex.getMethodId(directMethodsIdx);
+            directMethodsFlags[i] = BufferUtil.getULEB128(buffer);;
+            directMethodsCodeOff[i] = BufferUtil.getULEB128(buffer);;
         }
 
         // Parse encoded_method structures in virtual_methods array.
         virtualMethodsIds = new MethodId[virtualMethodsSize];
         virtualMethodsFlags = new int[virtualMethodsSize];
-        virtualMethodsCode = new Code[virtualMethodsSize];
+        virtualMethodsCodeOff = new int[virtualMethodsSize];
         int virtualMethodsIdx = 0;
         for (int i = 0; i < virtualMethodsSize; i++) {
             virtualMethodsIdx += BufferUtil.getULEB128(buffer);
-            MethodId method = dex.getMethodId(virtualMethodsIdx);
-            int flags = BufferUtil.getULEB128(buffer);
-            int codeOff = BufferUtil.getULEB128(buffer);
-            virtualMethodsIds[i] = method;
-            virtualMethodsFlags[i] = flags;
-            if (codeOff != 0)
-                virtualMethodsCode[i] = Code.parse(dex.getDataBuffer(codeOff),
-                        dex, method, flags);
+            virtualMethodsIds[i] = dex.getMethodId(virtualMethodsIdx);
+            virtualMethodsFlags[i] = BufferUtil.getULEB128(buffer);;
+            virtualMethodsCodeOff[i] = BufferUtil.getULEB128(buffer);;
         }
-
-        // Parse encoded_array_item and contained encoded_value structures.
-        int staticValuesOff = classDef.getStaticValuesOff();
-        if (staticValuesOff != 0) {
-            ByteBuffer buf = dex.getDataBuffer(staticValuesOff);
-            staticValues = EncodedValue.parseArray(buf, dex);
-        } else
-            staticValues = new Object[0];
-    }
-
-    public int getStaticFieldsSize() {
-        return staticFieldsSize;
-    }
-
-    public int getInstanceFieldsSize() {
-        return instanceFieldsSize;
     }
 
-    public int getDirectMethodsSize() {
-        return directMethodsSize;
-    }
-
-    public int getVirtualMethodsSize() {
-        return virtualMethodsSize;
-    }
-
-    public FieldId getStaticFieldsId(int idx) {
-        return staticFieldsIds[idx];
-    }
+    /**
+     * Returns the code object for a {@code code_item} at the given data offset.
+     * The underlying implementation uses a lazy parsing approach.
+     * 
+     * @param codeOff The given offset into the data area.
+     * @param method The method identifier of the method the code belongs to.
+     * @param flags The access flags of the method the code belongs to.
+     * @return The code object or {@code null} if there is none.
+     */
+    public Code getCode(int codeOff, MethodId method, int flags) {
+        if (codeOff == 0)
+            return null;
 
-    public int getStaticFieldsFlag(int idx) {
-        return staticFieldsFlags[idx];
-    }
-
-    public FieldId getInstanceFieldsId(int idx) {
-        return instanceFieldsIds[idx];
-    }
+        // Parse associated code_item structure.
+        ByteBuffer buf = dex.getDataBuffer(codeOff);
+        Code code = Code.parse(buf, dex, method, flags);
 
-    public int getInstanceFieldsFlag(int idx) {
-        return instanceFieldsFlags[idx];
-    }
-
-    public int getDirectMethodsFlag(int idx) {
-        return directMethodsFlags[idx];
-    }
-
-    public int getVirtualMethodsFlag(int idx) {
-        return virtualMethodsFlags[idx];
+        // Return the non-null object.
+        return code;
     }
 
     /**
@@ -208,6 +172,7 @@
     public void accept(DexClassVisitor visitor, int skip) {
 
         // Visit all static fields.
+        Object[] staticValues = classDef.getStaticValues();
         for (int i = 0; i < staticFieldsSize; i++) {
             int access = staticFieldsFlags[i];
             FieldId field = staticFieldsIds[i];
@@ -226,8 +191,8 @@
         for (int i = 0; i < directMethodsSize; i++) {
             int access = directMethodsFlags[i];
             MethodId method = directMethodsIds[i];
-            Code code = ((skip & DexReader.SKIP_CODE) == 0) ?
-                    directMethodsCode[i] : null;
+            Code code = ((skip & DexReader.SKIP_CODE) == 0) ? getCode(
+                    directMethodsCodeOff[i], method, access) : null;
             acceptMethod(visitor, access, method, code, skip);
         }
 
@@ -235,8 +200,8 @@
         for (int i = 0; i < virtualMethodsSize; i++) {
             int access = virtualMethodsFlags[i];
             MethodId method = virtualMethodsIds[i];
-            Code code = ((skip & DexReader.SKIP_CODE) == 0) ?
-                    virtualMethodsCode[i] : null;
+            Code code = ((skip & DexReader.SKIP_CODE) == 0) ? getCode(
+                    virtualMethodsCodeOff[i], method, access) : null;
             acceptMethod(visitor, access, method, code, skip);
         }
     }
--- a/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sun Apr 10 23:52:10 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sun Apr 10 23:54:27 2011 +0200
@@ -57,6 +57,8 @@
         return new ClassDef(buffer, dex);
     }
 
+    private final DexFile dex;
+
     private String className;
 
     private int accessFlags;
@@ -69,18 +71,19 @@
 
     private final AnnotationsDirectory annotations;
 
-    private ClassData classData;
+    private final int classDataOff;
 
     private final int staticValuesOff;
 
     private ClassDef(ByteBuffer buffer, DexFile dex) {
+        this.dex = dex;
         int classIdx = buffer.getInt();
         accessFlags = buffer.getInt();
         int superclassIdx = buffer.getInt();
         int interfacesOff = buffer.getInt();
         int sourceFileIdx = buffer.getInt();
         int annotationsOff = buffer.getInt();
-        int classDataOff = buffer.getInt();
+        classDataOff = buffer.getInt();
         staticValuesOff = buffer.getInt();
 
         // Resolve string and type indices.
@@ -104,12 +107,6 @@
             annotations = AnnotationsDirectory.parse(buf, dex);
         } else
             annotations = null;
-
-        // Parse associated class_data_item structure.
-        if (classDataOff != 0) {
-            ByteBuffer buf = dex.getDataBuffer(classDataOff);
-            classData = ClassData.parse(buf, dex, this);
-        }
     }
 
     public String getClassName() {
@@ -136,12 +133,65 @@
         return annotations;
     }
 
+    /**
+     * Returns the class data associated with this class definition. The
+     * underlying implementation uses a lazy parsing approach.
+     * 
+     * @return The class data object or {@code null} if there is none.
+     */
     public ClassData getClassData() {
+        if (classDataOff == 0)
+            return null;
+
+        /*
+         * XXX Should we at some point decide to use caching for data object,
+         * that is how it is done. Note that the method doesn't need to be
+         * synchronized, we are aware of the race-condition but can safely
+         * ignore it in this case.
+
+        // Retrieve previously parsed and cached object.
+        ClassData classData = (cachedClassData == null) ? null
+                : cachedClassData.get();
+
+        // In case there is no previously cached object (or it was collected),
+        // we parse the data in a lazy fashion and cache it.
+        if (classData == null) {
+            ByteBuffer buf = dex.getDataBuffer(classDataOff);
+            classData = ClassData.parse(buf, dex, this);
+            cachedClassData = new SoftReference<ClassData>(classData);
+        }
+
+        * ... but we do not use caching at the moment.
+        */
+
+        // Parse associated class_data_item structure.
+        ByteBuffer buf = dex.getDataBuffer(classDataOff);
+        ClassData classData = ClassData.parse(buf, dex, this);
+
+        // Return the non-null object.
         return classData;
     }
 
-    public int getStaticValuesOff() {
-        return staticValuesOff;
+    /** Cache for parsed class data object, allowed to be collected. */
+    //private SoftReference<ClassData> cachedClassData;
+
+    /**
+     * Returns the list of initial values for static fields. The underlying
+     * implementation uses a lazy parsing approach.
+     * 
+     * @return The array holding static field values or an empty array in case
+     *         there are none, never returns {@code null}.
+     */
+    public Object[] getStaticValues() {
+        if (staticValuesOff == 0)
+            return new Object[0];
+
+        // Parse encoded_array_item and contained encoded_value structures.
+        ByteBuffer buf = dex.getDataBuffer(staticValuesOff);
+        Object[] staticValues = EncodedValue.parseArray(buf, dex);
+
+        // Return the non-null object.
+        return staticValues;
     }
 
     /**
@@ -155,9 +205,9 @@
         if (sourceFile != null)
             visitor.visitSource(sourceFile);
         if (annotations != null && (skip & DexReader.SKIP_ANNOTATIONS) == 0)
-            annotations.acceptClassAnnotations(visitor);
-        if (classData != null)
-            classData.accept(visitor, skip);
+            getAnnotations().acceptClassAnnotations(visitor);
+        if (classDataOff != 0)
+            getClassData().accept(visitor, skip);
         visitor.visitEnd();
     }
 }
--- a/src/main/java/org/icedrobot/daneel/dex/Code.java	Sun Apr 10 23:52:10 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/Code.java	Sun Apr 10 23:54:27 2011 +0200
@@ -83,12 +83,12 @@
 
     private final int triesSize;
 
+    private final int debugInfoOff;
+
     private final int insnsSize;
 
     private final ShortBuffer insns;
 
-    private final DebugInfo debugInfo;
-
     private final List<TryCatchInfo> tryCatchInfos;
 
     private Code(ByteBuffer buffer, DexFile dex, MethodId method, int flags) {
@@ -99,7 +99,7 @@
         insSize = buffer.getShort();
         outsSize = buffer.getShort();
         triesSize = buffer.getShort();
-        int debugInfoOff = buffer.getInt();
+        debugInfoOff = buffer.getInt();
         insnsSize = buffer.getInt();
 
         // Keep a separate buffer for the instructions array.
@@ -149,13 +149,6 @@
                 }
             }
         }
-
-        // Parse any associated debug information.
-        if (debugInfoOff != 0) {
-            ByteBuffer buf = dex.getDataBuffer(debugInfoOff);
-            debugInfo = DebugInfo.parse(buf, dex, this);
-        } else
-            debugInfo = null;
     }
 
     /**
@@ -187,6 +180,24 @@
     }
 
     /**
+     * Returns the debug information associated with this code. The underlying
+     * implementation uses a lazy parsing approach.
+     * 
+     * @return The debug info object or {@code null} if there is none.
+     */
+    public DebugInfo getDebugInfo() {
+        if (debugInfoOff == 0)
+            return null;
+
+        // Parse any associated debug information.
+        ByteBuffer buf = dex.getDataBuffer(debugInfoOff);
+        DebugInfo debugInfo = DebugInfo.parse(buf, dex, this);
+
+        // Return the non-null object.
+        return debugInfo;
+    }
+
+    /**
      * Allows the given visitor to visit this code object.
      * 
      * @param visitor The given DEX method visitor object.
@@ -204,7 +215,8 @@
         acceptInsns(visitor);
 
         // Visit debug information if available and requested.
-        if (debugInfo != null && (skip & DexReader.SKIP_DEBUGINFO) == 0) {
+        if (debugInfoOff != 0 && (skip & DexReader.SKIP_DEBUGINFO) == 0) {
+            DebugInfo debugInfo = getDebugInfo();
             for (LocalVariable local : debugInfo.getLocalVariables())
                 visitor.visitLocalVariable(local.name, local.type,
                         local.startLabel, local.endLabel, local.regNum);