changeset 5:bfd49213d13c

Prototype for a class transformer using the ASM library. Contributed by Michael Starzinger.
author Mario Torre <neugens.limasoftware@gmail.com>
date Mon, 07 Mar 2011 20:38:30 +0100
parents 1308ee8666a2
children 2672c39e014c
files pom.xml src/main/java/org/icedrobot/daneel/ClassTransformer.java 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/DexFile.java src/main/java/org/icedrobot/daneel/dex/FieldId.java src/main/java/org/icedrobot/daneel/util/BufferUtil.java src/test/java/org/icedrobot/daneel/ClassTransformerTest.java
diffstat 8 files changed, 603 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/pom.xml	Mon Mar 07 20:30:39 2011 +0100
+++ b/pom.xml	Mon Mar 07 20:38:30 2011 +0100
@@ -46,6 +46,11 @@
   </build>
   <dependencies>
     <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>3.3</version>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.8.2</version>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/ClassTransformer.java	Mon Mar 07 20:38:30 2011 +0100
@@ -0,0 +1,85 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.icedrobot.daneel;
+
+import org.icedrobot.daneel.dex.ClassData;
+import org.icedrobot.daneel.dex.ClassDef;
+import org.icedrobot.daneel.dex.FieldId;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+public class ClassTransformer {
+
+    /**
+     * Transforms a given class from Dalvik representation into its equivalent
+     * Java bytecode representation.
+     * @param classDef The given class definition as Dalvik representation.
+     * @return The generated Java bytecode representation.
+     */
+    public static byte[] transformClass(ClassDef classDef) {
+      ClassTransformer transformer = new ClassTransformer(classDef);
+      transformer.visitClass();
+      transformer.visitFields();
+      return transformer.visitEnd();
+    }
+
+    private ClassWriter writer;
+
+    private ClassDef classDef;
+
+    private ClassTransformer(ClassDef classDef) {
+        super();
+        this.writer = new ClassWriter(0);
+        this.classDef = classDef;
+    }
+
+    private void visitClass() {
+        int access = classDef.getAccessFlags();
+        String name = extractName(classDef.getClassName());
+        String superName = extractName(classDef.getSuperclass());
+        writer.visit(Opcodes.V1_6, access, name, null, superName, null);
+    }
+
+    private void visitFields() {
+        ClassData classData = classDef.getClassData();
+        for (int i = 0; i < classData.getStaticFieldsSize(); i++) {
+            int access = classData.getStaticFieldsFlag(i);
+            FieldId field = classData.getStaticFieldsId(i);
+            writer.visitField(access, field.getName(), field.getTypeDescriptor(), null, null);
+        }
+    }
+
+    private byte[] visitEnd() {
+        writer.visitEnd();
+        return writer.toByteArray();
+    }
+
+    /**
+     * Converts a type descriptor for a class type into an internal class name
+     * as the ASM library expects it. For details about those type names see
+     * the "ASM 3.0 User Guide, Section 2.1.2 and 2.1.3".
+     * @param desc The given type descriptor.
+     * @return The internal class name.
+     */
+    private static String extractName(String desc) {
+        if (!desc.startsWith("L") || !desc.endsWith(";"))
+            throw new DaneelException("Descriptor is not a class type.");
+        return desc.substring(1, desc.length() - 1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Mon Mar 07 20:38:30 2011 +0100
@@ -0,0 +1,148 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.icedrobot.daneel.dex;
+
+import java.nio.ByteBuffer;
+
+import org.icedrobot.daneel.util.BufferUtil;
+
+public class ClassData {
+
+    /**
+     * Parses a {@code class_data_item} structure in a DEX file at the buffer's
+     * current position.
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    static ClassData parse(ByteBuffer buffer, DexFile dex) {
+        return new ClassData(buffer, dex);
+    }
+
+    private int staticFieldsSize;
+
+    private int instanceFieldsSize;
+
+    private int directMethodsSize;
+
+    private int virtualMethodsSize;
+
+    private FieldId[] staticFieldsIds;
+    private int[] staticFieldsFlags;
+
+    private FieldId[] instanceFieldsIds;
+    private int[] instanceFieldsFlags;
+
+    private int[] directMethodsIdxDiff;
+    private int[] directMethodsFlags;
+    private int[] directMethodsCodeOff;
+
+    private int[] virtualMethodsIdxDiff;
+    private int[] virtualMethodsFlags;
+    private int[] virtualMethodsCodeOff;
+
+    private ClassData(ByteBuffer buffer, DexFile dex) {
+        staticFieldsSize = BufferUtil.getULEB128(buffer);
+        instanceFieldsSize = BufferUtil.getULEB128(buffer);
+        directMethodsSize = BufferUtil.getULEB128(buffer);
+        virtualMethodsSize = BufferUtil.getULEB128(buffer);
+
+        // Parse encoded_field structures in static_fields array and resolve
+        // field id indices.
+        staticFieldsIds = new FieldId[staticFieldsSize];
+        staticFieldsFlags = new int[staticFieldsSize];
+        int staticFieldsIdx = 0;
+        for (int i = 0; i < staticFieldsSize; i++) {
+            staticFieldsIdx += BufferUtil.getULEB128(buffer);
+            staticFieldsIds[i] = dex.getFieldId(staticFieldsIdx);
+            staticFieldsFlags[i] = BufferUtil.getULEB128(buffer);
+        }
+
+        // Parse encoded_field structures in instance_fields array and resolve
+        // field id indices.
+        instanceFieldsIds = new FieldId[instanceFieldsSize];
+        instanceFieldsFlags = new int[instanceFieldsSize];
+        int instanceFieldsIdx = 0;
+        for (int i = 0; i < instanceFieldsSize; i++) {
+            instanceFieldsIdx += BufferUtil.getULEB128(buffer);
+            instanceFieldsIds[i] = dex.getFieldId(instanceFieldsIdx);
+            instanceFieldsFlags[i] = BufferUtil.getULEB128(buffer);
+        }
+
+        // Parse encoded_method structures in direct_methods array.
+        directMethodsIdxDiff = new int[directMethodsSize];
+        directMethodsFlags = new int[directMethodsSize];
+        directMethodsCodeOff = new int[directMethodsSize];
+        for (int i = 0; i < directMethodsSize; i++) {
+            directMethodsIdxDiff[i] = BufferUtil.getULEB128(buffer);
+            directMethodsFlags[i] = BufferUtil.getULEB128(buffer);
+            directMethodsCodeOff[i] = BufferUtil.getULEB128(buffer);
+        }
+
+        // Parse encoded_method structures in virtual_methods array.
+        virtualMethodsIdxDiff = new int[virtualMethodsSize];
+        virtualMethodsFlags = new int[virtualMethodsSize];
+        virtualMethodsCodeOff = new int[virtualMethodsSize];
+        for (int i = 0; i < virtualMethodsSize; i++) {
+            virtualMethodsIdxDiff[i] = BufferUtil.getULEB128(buffer);
+            virtualMethodsFlags[i] = BufferUtil.getULEB128(buffer);
+            virtualMethodsCodeOff[i] = BufferUtil.getULEB128(buffer);
+        }
+    }
+
+    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];
+    }
+
+    public int getStaticFieldsFlag(int idx) {
+        return staticFieldsFlags[idx];
+    }
+
+    public FieldId getInstanceFieldsId(int idx) {
+        return instanceFieldsIds[idx];
+    }
+
+    public int getInstanceFieldsFlag(int idx) {
+        return instanceFieldsFlags[idx];
+    }
+
+    public int getDirectMethodsFlag(int idx) {
+        return directMethodsFlags[idx];
+    }
+
+    public int getVirtualMethodsFlag(int idx) {
+        return virtualMethodsFlags[idx];
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Mon Mar 07 20:38:30 2011 +0100
@@ -0,0 +1,102 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.icedrobot.daneel.dex;
+
+import java.nio.ByteBuffer;
+
+public class ClassDef {
+
+    /**
+     * Parses a {@code class_def_item} structure in a DEX file at the buffer's
+     * current position.
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    static ClassDef parse(ByteBuffer buffer, DexFile dex) {
+        return new ClassDef(buffer, dex);
+    }
+
+    private String className;
+
+    private int accessFlags;
+
+    private String superclass;
+
+    private String[] interfaces;
+
+    private String sourceFile;
+
+    private ClassData classData;
+
+    private ClassDef(ByteBuffer buffer, DexFile 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();
+        int staticValuesOff = buffer.getInt();
+
+        // Resolve string and type indices.
+        className = dex.getTypeDescriptor(classIdx);
+        if (superclassIdx != DexFile.NO_INDEX)
+            superclass = dex.getTypeDescriptor(superclassIdx);
+        if (sourceFileIdx != DexFile.NO_INDEX)
+            sourceFile = dex.getString(sourceFileIdx);
+
+        // Parse associated type_list structure and resolve type indices.
+        ByteBuffer buf = buffer.duplicate();
+        if (interfacesOff != 0) {
+            buf.position(interfacesOff);
+            interfaces = new String[buf.getInt()];
+            for (int i = 0; i < interfaces.length; i++)
+                interfaces[i] = dex.getTypeDescriptor(buf.getShort());
+        }
+
+        // Parse the associated class_data_item structure.
+        buf.position(classDataOff);
+        classData = ClassData.parse(buf, dex);
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    public String getSuperclass() {
+        return superclass;
+    }
+
+    public String[] getInterfaces() {
+        return interfaces;
+    }
+
+    public String getSourceFile() {
+        return sourceFile;
+    }
+
+    public ClassData getClassData() {
+        return classData;
+    }
+}
--- a/src/main/java/org/icedrobot/daneel/dex/DexFile.java	Mon Mar 07 20:30:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DexFile.java	Mon Mar 07 20:38:30 2011 +0100
@@ -2,23 +2,86 @@
 
 import java.nio.ByteBuffer;
 
-class DexFile {
+import org.icedrobot.daneel.util.BufferUtil;
+
+public class DexFile {
+
+    /** Constant used to indicate that an index value is absent. */
+    public static final int NO_INDEX = 0xffffffff;
 
-    static DexFile parse(ByteBuffer buf) {
+    public static DexFile parse(ByteBuffer buf) {
         return new DexFile(buf);
     }
 
+    public static DexFile parse(byte[] bytes) {
+        return new DexFile(ByteBuffer.wrap(bytes));
+    }
+
     private Header header;
 
-    private DexFile(ByteBuffer buf) {
-        ByteBuffer headerBuffer = buf.duplicate();
-        header = Header.parse(headerBuffer);
+    private String[] strings;
+
+    private String[] typeDescriptors;
+
+    private FieldId[] fieldIds;
+
+    private ClassDef[] classDefs;
+
+    private DexFile(ByteBuffer buffer) {
+        ByteBuffer buf = buffer.duplicate();
+        header = Header.parse(buf);
+
+        // Parse string_id_item and string_data_item structures.
+        strings = new String[header.getStringIdsSize()];
+        buf.position(header.getStringIdsOff());
+        int[] stringsDataOff = BufferUtil.getInts(buf, strings.length);
+        for (int i = 0; i < strings.length; i++) {
+            buf.position(stringsDataOff[i]);
+            int utf16Size = BufferUtil.getULEB128(buf);
+            strings[i] = BufferUtil.getMUTF8(buf);
+            if (strings[i].length() != utf16Size)
+                throw new DexParseException("Mismatch in string lengths.");
+        }
+
+        // Parse type_id_item structures.
+        typeDescriptors = new String[header.getTypeIdsSize()];
+        buf.position(header.getTypeIdsOff());
+        for (int i = 0; i < typeDescriptors.length; i++)
+            typeDescriptors[i] = getString(buf.getInt());
+
+        // Parse the field_id_item structures.
+        fieldIds = new FieldId[header.getFieldIdsSize()];
+        buf.position(header.getFieldIdsOff());
+        for (int i = 0; i < fieldIds.length; i++)
+            fieldIds[i] = FieldId.parse(buf, this);
+
+        // Parse class_def_item structures.
+        classDefs = new ClassDef[header.getClassDefsSize()];
+        buf.position(header.getClassDefsOff());
+        for (int i = 0; i < classDefs.length; i++)
+            classDefs[i] = ClassDef.parse(buf, this);
     }
 
     Header getHeader() {
         return header;
     }
 
+    public String getString(int idx) {
+        return strings[idx];
+    }
+
+    public String getTypeDescriptor(int idx) {
+        return typeDescriptors[idx];
+    }
+
+    public FieldId getFieldId(int idx) {
+        return fieldIds[idx];
+    }
+
+    public ClassDef getClassDef(int idx) {
+        return classDefs[idx];
+    }
+
     public String toString() {
         return header.toString();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/dex/FieldId.java	Mon Mar 07 20:38:30 2011 +0100
@@ -0,0 +1,59 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.icedrobot.daneel.dex;
+
+import java.nio.ByteBuffer;
+
+public class FieldId {
+
+    /**
+     * Parses a {@code field_id_item} structure in a DEX file at the buffer's
+     * current position.
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    static FieldId parse(ByteBuffer buffer, DexFile dex) {
+        return new FieldId(buffer, dex);
+    }
+
+    private final String className;
+
+    private final String typeDescriptor;
+
+    private final String name;
+
+    private FieldId(ByteBuffer buffer, DexFile dex) {
+        className = dex.getTypeDescriptor(buffer.getShort());
+        typeDescriptor = dex.getTypeDescriptor(buffer.getShort());
+        name = dex.getString(buffer.getInt());
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getTypeDescriptor() {
+        return typeDescriptor;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
--- a/src/main/java/org/icedrobot/daneel/util/BufferUtil.java	Mon Mar 07 20:30:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/util/BufferUtil.java	Mon Mar 07 20:38:30 2011 +0100
@@ -18,7 +18,9 @@
 
 package org.icedrobot.daneel.util;
 
+import java.io.ByteArrayOutputStream;
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
 
 public class BufferUtil {
 
@@ -53,4 +55,21 @@
         }
         return result;
     }
+
+    /**
+     * Reads string encoded using the "Modified UTF-8" encoding at the buffer's
+     * current position. The number of bytes read from the buffer is implied by
+     * the position of the terminating 0 byte.
+     * @param buffer The byte buffer to read from.
+     * @return The decoded string value.
+     */
+    public static String getMUTF8(ByteBuffer buffer) {
+        byte b;
+        ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+        while ((b = buffer.get()) != 0)
+            tmp.write(b);
+        return new String(tmp.toByteArray(), UTF8);
+    }
+
+    private static final Charset UTF8 = Charset.forName("UTF-8");
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/java/org/icedrobot/daneel/ClassTransformerTest.java	Mon Mar 07 20:38:30 2011 +0100
@@ -0,0 +1,117 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.icedrobot.daneel;
+
+import static org.junit.Assert.assertEquals;
+
+import org.icedrobot.daneel.dex.ClassDef;
+import org.icedrobot.daneel.dex.DexFile;
+import org.junit.Test;
+
+public class ClassTransformerTest extends ClassLoader {
+
+    @Test
+    public void testTransformClassStaticFields() throws Exception {
+        ClassDef classDef = DexFile.parse(TEST_STATIC_FIELDS).getClassDef(0);
+        byte[] bytecode = ClassTransformer.transformClass(classDef);
+        Class<?> cls = defineClass(null, bytecode, 0, bytecode.length);
+        assertEquals(7, cls.getFields().length);
+        assertEquals("public static byte StaticFieldTest.staticByte",
+                cls.getField("staticByte").toString());
+        assertEquals("public static short StaticFieldTest.staticShort",
+                cls.getField("staticShort").toString());
+        assertEquals("public static int StaticFieldTest.staticInt",
+                cls.getField("staticInt").toString());
+        assertEquals("public static long StaticFieldTest.staticLong",
+                cls.getField("staticLong").toString());
+        assertEquals("public static float StaticFieldTest.staticFloat",
+                cls.getField("staticFloat").toString());
+        assertEquals("public static double StaticFieldTest.staticDouble",
+                cls.getField("staticDouble").toString());
+        assertEquals("public static java.lang.Object StaticFieldTest.staticObject",
+                cls.getField("staticObject").toString());
+    }
+
+    /**
+     * Test data containing the DEX file derived from the following source:
+     * <code><pre>public class StaticFieldTest {
+     *    public static byte staticByte;
+     *    public static short staticShort;
+     *    public static int staticInt;
+     *    public static long staticLong;
+     *    public static float staticFloat;
+     *    public static double staticDouble;
+     *    public static Object staticObject;
+     * }</pre></code>
+     */
+    private static final byte[] TEST_STATIC_FIELDS = new byte[] {
+         100, 101, 120,  10,  48,  51,  53,   0,  38,  92, -84, -49,  38,  96,
+          19,  12, -90,-123, -66,  81,  46, -61,-122, -38,  56,  23,  99, -23,
+          42, -14, -71, -28, -52,   2,   0,   0, 112,   0,   0,   0, 120,  86,
+          52,  18,   0,   0,   0,   0,   0,   0,   0,   0,  56,   2,   0,   0,
+          18,   0,   0,   0, 112,   0,   0,   0,   9,   0,   0,   0, -72,   0,
+           0,   0,   1,   0,   0,   0, -36,   0,   0,   0,   7,   0,   0,   0,
+         -24,   0,   0,   0,   2,   0,   0,   0,  32,   1,   0,   0,   1,   0,
+           0,   0,  48,   1,   0,   0, 124,   1,   0,   0,  80,   1,   0,   0,
+         104,   1,   0,   0, 112,   1,   0,   0, 115,   1,   0,   0, 118,   1,
+           0,   0, 121,   1,   0,   0, 124,   1,   0,   0, 127,   1,   0,   0,
+        -110,   1,   0,   0, -90,   1,   0,   0, -87,   1,   0,   0, -65,   1,
+           0,   0, -62,   1,   0,   0, -50,   1,   0,   0, -36,   1,   0,   0,
+         -23,   1,   0,   0, -12,   1,   0,   0,   0,   2,   0,   0,  14,   2,
+           0,   0,   1,   0,   0,   0,   2,   0,   0,   0,   3,   0,   0,   0,
+           4,   0,   0,   0,   5,   0,   0,   0,   6,   0,   0,   0,   7,   0,
+           0,   0,   8,   0,   0,   0,  10,   0,   0,   0,  10,   0,   0,   0,
+           8,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,  11,   0,
+           0,   0,   5,   0,   1,   0,  12,   0,   0,   0,   5,   0,   2,   0,
+          13,   0,   0,   0,   5,   0,   3,   0,  14,   0,   0,   0,   5,   0,
+           4,   0,  15,   0,   0,   0,   5,   0,   6,   0,  16,   0,   0,   0,
+           5,   0,   7,   0,  17,   0,   0,   0,   5,   0,   0,   0,   0,   0,
+           0,   0,   6,   0,   0,   0,   0,   0,   0,   0,   5,   0,   0,   0,
+           1,   0,   0,   0,   6,   0,   0,   0,   0,   0,   0,   0,   9,   0,
+           0,   0,   0,   0,   0,   0,  32,   2,   0,   0,   0,   0,   0,   0,
+           1,   0,   1,   0,   1,   0,   0,   0,  27,   2,   0,   0,   4,   0,
+           0,   0, 112,  16,   1,   0,   0,   0,  14,   0,   6,  60, 105, 110,
+         105, 116,  62,   0,   1,  66,   0,   1,  68,   0,   1,  70,   0,   1,
+          73,   0,   1,  74,   0,  17,  76,  83, 116,  97, 116, 105,  99,  70,
+         105, 101, 108, 100,  84, 101, 115, 116,  59,   0,  18,  76, 106,  97,
+         118,  97,  47, 108,  97, 110, 103,  47,  79,  98, 106, 101,  99, 116,
+          59,   0,   1,  83,   0,  20,  83, 116,  97, 116, 105,  99,  70, 105,
+         101, 108, 100,  84, 101, 115, 116,  46, 106,  97, 118,  97,   0,   1,
+          86,   0,  10, 115, 116,  97, 116, 105,  99,  66, 121, 116, 101,   0,
+          12, 115, 116,  97, 116, 105,  99,  68, 111, 117,  98, 108, 101,   0,
+          11, 115, 116,  97, 116, 105,  99,  70, 108, 111,  97, 116,   0,   9,
+         115, 116,  97, 116, 105,  99,  73, 110, 116,   0,  10, 115, 116,  97,
+         116, 105,  99,  76, 111, 110, 103,   0,  12, 115, 116,  97, 116, 105,
+          99,  79,  98, 106, 101,  99, 116,   0,  11, 115, 116,  97, 116, 105,
+          99,  83, 104, 111, 114, 116,   0,   1,   0,   7,  14,   0,   7,   0,
+           1,   0,   0,   9,   1,   9,   1,   9,   1,   9,   1,   9,   1,   9,
+           1,   9,   0,-127,-128,   4, -48,   2,  12,   0,   0,   0,   0,   0,
+           0,   0,   1,   0,   0,   0,   0,   0,   0,   0,   1,   0,   0,   0,
+          18,   0,   0,   0, 112,   0,   0,   0,   2,   0,   0,   0,   9,   0,
+           0,   0, -72,   0,   0,   0,   3,   0,   0,   0,   1,   0,   0,   0,
+         -36,   0,   0,   0,   4,   0,   0,   0,   7,   0,   0,   0, -24,   0,
+           0,   0,   5,   0,   0,   0,   2,   0,   0,   0,  32,   1,   0,   0,
+           6,   0,   0,   0,   1,   0,   0,   0,  48,   1,   0,   0,   1,  32,
+           0,   0,   1,   0,   0,   0,  80,   1,   0,   0,   2,  32,   0,   0,
+          18,   0,   0,   0, 104,   1,   0,   0,   3,  32,   0,   0,   1,   0,
+           0,   0,  27,   2,   0,   0,   0,  32,   0,   0,   1,   0,   0,   0,
+          32,   2,   0,   0,   0,  16,   0,   0,   1,   0,   0,   0,  56,   2,
+           0,   0
+    };
+}