Exprimons le résultat de l'analyse du code d'octet Java dans un diagramme de classes

Exprimer le résultat de l'analyse du code d'octet Java avec un diagramme de classes ou un graphe d'appel

Objectif

Extrayez les informations de classe et de méthode à partir du code d'octet Java et enregistrez-le dans Sqlite. Décrivez le diagramme de classe et le graphe d'appel en utilisant les informations enregistrées ci-dessus.

Si vous pouvez utiliser doxygen + graphviz, vous devez l'utiliser.

environnement d'utilisation

・ Windows10 ・ Java8 ・ Bcel-6.3.1. ・ Sqlite-jdbc-3.27.2.1 ・ Plantuml.jar

Explication de la bibliothèque utilisée

bcel L'API BCEL (Byte Code Engineering Library) est une boîte à outils pour l'analyse statique et la création dynamique de fichiers de classe Java. FindBug utilise BCEL pour effectuer une analyse statique à partir de fichiers de classe.

bcel.png

https://commons.apache.org/proper/commons-bcel/manual/introduction.html

Exemple de code

JavaSE BCEL https://hondou.homedns.org/pukiwiki/index.php?JavaSE%20BCEL

En outre, le codeToString suivant sera utile pour savoir comment interpréter le binaire de code d'octet. https://github.com/llmhyy/commons-bcel/blob/master/src/main/java/org/apache/bcel/classfile/Utility.java

Sqlite-JDBC Vous pouvez le télécharger à partir de la page suivante avec JDBC qui exploite SQLite.

Télécharger https://bitbucket.org/xerial/sqlite-jdbc/downloads/

Exemple de code

package sqlitesample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.sqlite.Function;

public class SqliteSample {
	public static void main(String[] args) throws ClassNotFoundException {
		System.out.println("start-----");
		// load the sqlite-JDBC driver using the current class loader
		Class.forName("org.sqlite.JDBC");

		Connection connection = null;
		Statement statement = null;
		ResultSet rs = null;
		PreparedStatement pstmt = null;

		try
		{
			// create a database connection
			// Connection connection = DriverManager.getConnection("jdbc:sqlite:C:/work/mydatabase.db");
			// Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
			connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
			connection.setAutoCommit(true);
			statement = connection.createStatement();
			statement.setQueryTimeout(30);	// set timeout to 30 sec.

			System.out.println("create table -----");
			statement.executeUpdate("drop table if exists person");
			statement.executeUpdate("create table person (id integer, name string)");
			statement.executeUpdate("insert into person values(1, 'leo')");
			statement.executeUpdate("insert into person values(2, 'yui')");
			rs = statement.executeQuery("select * from person");
			while(rs.next())
			{
				// read the result set
				System.out.println("name = " + rs.getString("name"));
				System.out.println("id = " + rs.getInt("id"));
			}
			rs.close();
			rs = null;

			System.out.println("update -----");
			pstmt = connection.prepareStatement("update person set name = ? where id = ?");
			pstmt.setString(1, "ole");
			pstmt.setInt(2, 1);
			pstmt.executeUpdate();
			rs = statement.executeQuery("select * from person");
			while(rs.next())
			{
				// read the result set
				System.out.println("name = " + rs.getString("name"));
				System.out.println("id = " + rs.getInt("id"));
			}
			rs.close();
			rs = null;


			System.out.println("Transactions -----");
			connection.setAutoCommit(false);
			statement.executeUpdate("insert into person values(3, 'zoo')");
			rs = statement.executeQuery("select * from person");
			while(rs.next())
			{
				// read the result set
				System.out.println("name = " + rs.getString("name"));
				System.out.println("id = " + rs.getInt("id"));
			}
			rs.close();
			rs = null;
			System.out.println("rollback -----");
			connection.rollback();
			rs = statement.executeQuery("select * from person");
			while(rs.next())
			{
				// read the result set
				System.out.println("name = " + rs.getString("name"));
				System.out.println("id = " + rs.getInt("id"));
			}
			rs.close();
			rs = null;

			System.out.println("function -----");
	        Function.create(connection, "total", new Function() {
	            @Override
	            protected void xFunc() throws SQLException
	            {
	                int sum = 0;
	                for (int i = 0; i < args(); i++)
	                    sum += value_int(i);
	                result(sum);
	            }
	        });
			rs = statement.executeQuery("select total(1, 2, 3, 4, 5)");
			while(rs.next())
			{
				// read the result set
				System.out.println("total(1,2,3,4,5) = " + rs.getInt(1));
			}
			rs.close();
			rs = null;

		}
		catch(SQLException e)
		{
			// if the error message is "out of memory",
			// it probably means no database file is found
			System.err.println(e.getMessage());
		}
		finally
		{
			try
			{
				if(rs != null)
				{
					rs.close();
				}
				if(pstmt != null)
				{
					pstmt.close();
				}
				if(statement != null)
				{
					statement.close();
				}
				if(connection != null)
				{
				connection.close();
				}
			}
			catch(SQLException e)
			{
				// connection close failed.
				System.err.println(e);
			}
		}
	}
}

Vous pouvez également créer des fonctions définies par l'utilisateur. En outre, le code de test suivant sera utile pour les opérations spécifiques à SQLite. https://github.com/xerial/sqlite-jdbc/tree/c7c5604bcc584460268abc9a64df2953fca788d3/src/test/java/org/sqlite

PlantUML Vous pouvez écrire la figure suivante incluant UML dans un langage appelé DSL.

Vous pouvez officiellement essayer la description réelle de la page Web. Il existe également un plug-in Redmine, vous pouvez donc intégrer diverses figures sous forme de données texte dans le Wiki.

officiel: http://plantuml.com/ja/

Télécharger http://plantuml.com/ja/download

échantillon

PlantUML Cheat Sheet https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48

Vous pouvez également générer des fichiers PNG et SVG à partir de votre propre programme en incorporant le fichier plantuml.jar dans votre projet Java. http://plantuml.com/ja/api

Résultat expérimental

https://github.com/mima3/BcelToSqlite

Enregistrer Sqlite en utilisant bcel

Le code ci-dessous spécifie un fichier Jar, analyse le fichier de classe qui y est stocké et enregistre les informations de classe, de méthode et de champ dans SQLite.

BcelToSqlite.java



package bcelToSqlite;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.bcel.Const;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Utility;
import org.apache.bcel.generic.Type;
import org.apache.bcel.util.ByteSequence;

public class BcelToSqlite {
	Connection connection = null;
	PreparedStatement pstmt = null;
	int nextClassId = 1;
	int nextMethodId = 0x10000001;
	int nextAnnotationId = 0x20000001;

	/**
	 *Mis en œuvre en référence à ce qui suit
	 * https://hondou.homedns.org/pukiwiki/index.php?JavaSE%20BCEL
	 * @param args
	 * @throws Exception
	 */
	public static void main(String args[]) throws Exception
	{
		String srcPath;
		srcPath = ".\\lib\\bcel-6.3.1.jar";
        BcelToSqlite thisClass = new BcelToSqlite();
        thisClass.startWalk(new File(srcPath));
        System.out.println(srcPath + " -> output.sqlite");
	}

	private void executeSql(String sql) throws SQLException {
		pstmt = connection.prepareStatement(sql);
		pstmt.executeUpdate();
		pstmt.close();
		pstmt = null;
	}
	private void executeSql(String sql, Object args[]) throws SQLException {
		pstmt = connection.prepareStatement(sql);
		int ix = 1;
		for (Object obj : args) {
			try {
				pstmt.setInt(ix, (int)Integer.parseInt(obj.toString()));
			} catch (NumberFormatException ex) {
				pstmt.setString(ix, obj.toString());
			}

			++ix;
		}
		pstmt.executeUpdate();
		pstmt.close();
		pstmt = null;
	}

	private void startWalk(File path) throws Exception {
		try {
			connection = DriverManager.getConnection("jdbc:sqlite:output.sqlite");
			connection.setAutoCommit(true);
			executeSql("drop table if exists class");
			executeSql("create table class (id int primary key, name string , access_flags int, super_class_name string)");

			executeSql("drop table if exists interface");
			executeSql("create table interface (class_id int, interface_name string)");

			executeSql("drop index if exists index_interface");
			executeSql("create index index_interface on interface(class_id)");

			executeSql("drop table if exists method");
			executeSql("create table method (id int primary key, class_id int, name string, fullname string, access_flag int, return_type string, byte_code string)");

			executeSql("drop table if exists method_parameter");
			executeSql("create table method_parameter (method_id int, seq int, param_type string)");

			executeSql("drop index if exists index_method_parameter");
			executeSql("create index index_method_parameter on method_parameter(method_id)");

			executeSql("drop table if exists method_depend");
			executeSql("create table method_depend (method_id int, called_method string, opecode int)");

			executeSql("drop index if exists index_method_depend");
			executeSql("create index index_method_depend on method_depend(method_id)");

			executeSql("drop table if exists field");
			executeSql("create table field (id int primary key, class_id int, name string, fullname string, access_flag int, type string)");

			executeSql("drop table if exists anotation");
			executeSql("create table anotation (id int primary key, refid int, type string)");

			executeSql("drop index if exists index_anotation");
			executeSql("create index index_anotation on anotation(refid)");

			connection.setAutoCommit(false);
			dirWalk(path);
	        connection.commit();

		}
		finally
		{
			if(pstmt != null)
			{
				pstmt.close();
			}
			if(connection != null)
			{
				connection.close();
			}
		}
	}
    private void dirWalk(File path) throws Exception {
    	if (path.isDirectory()) {
    		for (File child : path.listFiles()) {
    			dirWalk(child);
	        }
    	} else if (path.getName().endsWith("jar") || path.getName().endsWith("zip")) {
    		jarWalk(path);
    	} else if (path.getName().endsWith("class")) {
    		JavaClass javaClass = new ClassParser(path.getAbsolutePath()).parse();
	        classWalk(javaClass);
    	}
    }

    private void jarWalk(File jarFile) throws Exception {
        try (JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFile));) {
            JarEntry entry;
            while ((entry = jarIn.getNextJarEntry()) != null) {
                if (!entry.isDirectory()) {
                    String fileName = entry.getName();
                    if (fileName.endsWith("class")) {
                        JavaClass javaClass = new ClassParser(jarFile.getAbsolutePath(), fileName).parse();
                        classWalk(javaClass);
                    }
                }
            }
        }
    }

    private void classWalk(final JavaClass javaClass) throws SQLException, ClassNotFoundException, IOException {
    	System.out.println(javaClass.getClassName());

    	executeSql(
    		"insert into class values(?, ?, ?, ?)",
    		new Object[] {
    			nextClassId,
    			javaClass.getClassName(),
    			javaClass.getAccessFlags(),
    			javaClass.getSuperclassName()
    		}
    	);

    	//Get méthode
        final org.apache.bcel.classfile.Method[] methods = javaClass.getMethods();
        for (org.apache.bcel.classfile.Method method : methods) {
            methodWalk(nextClassId, javaClass, method);
        }

        Field[] fields = javaClass.getFields();
        for (Field field : fields) {
            fieldWalk(nextClassId, javaClass, field);
        }

        //Obtenir l'interface
        for (JavaClass i : javaClass.getAllInterfaces()) {
        	if (i.getClassName().equals(javaClass.getClassName())) {
        		continue;
        	}
			executeSql(
				"insert into interface values(?, ?)",
				new Object[] {
						nextClassId,
					i.getClassName()
				}
			);
        }

        //Annotation
        anotationWalk(nextClassId, javaClass.getAnnotationEntries());

        if (nextClassId % 500 == 0) {
        	connection.commit();
        }

        //commettre
        ++nextClassId;
    }

    private void anotationWalk(final int refId, final AnnotationEntry[] annotations) throws SQLException {
        for (AnnotationEntry a : annotations) {
        	executeSql(
            		"insert into anotation values(?, ?, ?)",
            		new Object[] {
            				nextAnnotationId,
            				refId,
            				a.getAnnotationType()
            		}
            	);
        }
        ++nextAnnotationId;

    }

    private void methodWalk(final int classId, final JavaClass javaClass, final org.apache.bcel.classfile.Method method) throws SQLException, IOException {
		String code = getCode(method);
    	executeSql(
    		"insert into method values(?, ?,  ?, ?, ?, ?, ?)",
    		new Object[] {
    				nextMethodId,
    				classId,
    				method.getName(),
    				javaClass.getClassName() + "." + method.getName() + " " + method.getSignature(),
    				method.getAccessFlags(),
    				method.getReturnType().toString(),
    				code
    			}
    	);

		int seq = 1;
		for(Type p : method.getArgumentTypes()) {
			executeSql(
				"insert into method_parameter values(?, ?, ?)",
				new Object[] {
					nextMethodId,
					seq,
					p.toString()
				}
			);
			++seq;
		}
		if (method.getCode() != null) {
			ByteSequence stream = new ByteSequence(method.getCode().getCode());
			for (int i = 0; stream.available() > 0 ; i++) {
				analyzeCode(nextMethodId, stream, method.getConstantPool());
			}
		}


        //Annotation
        anotationWalk(nextMethodId, method.getAnnotationEntries());

		++nextMethodId;
    }

    private void fieldWalk(final int classId, final JavaClass javaClass, final org.apache.bcel.classfile.Field field) throws SQLException, IOException {

    	executeSql(
    		"insert into field values(?, ?, ?, ?, ?, ?)",
    		new Object[] {
    				nextMethodId,
    				classId,
    				field.getName(),
    				javaClass.getClassName() + "." + field.getName() + " " + field.getSignature(),
    				field.getAccessFlags(),
    				field.getType().toString()
    			}
    	);

        //Annotation
        anotationWalk(nextMethodId, field.getAnnotationEntries());

		++nextMethodId;
    }

    private  boolean wide = false; /* The `WIDE' instruction is used in the
     * byte code to allow 16-bit wide indices
     * for local variables. This opcode
     * precedes an `ILOAD', e.g.. The opcode
     * immediately following takes an extra
     * byte which is combined with the
     * following byte to form a
     * 16-bit value.
     */

    /**
     *Mis en œuvre pour référence ci-dessous
     * commons-bcel/src/main/java/org/apache/bcel/classfile/Utility.java
     * codeToString
     * @param bytes
     * @param constant_pool
     * @throws IOException
     * @throws SQLException
     * @throws ClassFormatException
     */
    public  void analyzeCode(final int methodId,  final ByteSequence bytes, final ConstantPool constant_pool) throws IOException, ClassFormatException, SQLException {
        final short opcode = (short) bytes.readUnsignedByte();
        int default_offset = 0;
        int low;
        int high;
        int npairs;
        int index;
        int vindex;
        int constant;
        int[] match;
        int[] jump_table;
        int no_pad_bytes = 0;
        int offset;
        final boolean verbose = true;
        final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode));
        /* Special case: Skip (0-3) padding bytes, i.e., the
         * following bytes are 4-byte-aligned
         */
        if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) {
            final int remainder = bytes.getIndex() % 4;
            no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
            for (int i = 0; i < no_pad_bytes; i++) {
                byte b;
                if ((b = bytes.readByte()) != 0) {
                    System.err.println("Warning: Padding byte != 0 in "
                            + Const.getOpcodeName(opcode) + ":" + b);
                }
            }
            // Both cases have a field default_offset in common
            default_offset = bytes.readInt();
        }
        switch (opcode) {
            /* Table switch has variable length arguments.
             */
            case Const.TABLESWITCH:
                low = bytes.readInt();
                high = bytes.readInt();
                offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
                default_offset += offset;
                buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low)
                        .append(", high = ").append(high).append("(");
                jump_table = new int[high - low + 1];
                for (int i = 0; i < jump_table.length; i++) {
                    jump_table[i] = offset + bytes.readInt();
                    buf.append(jump_table[i]);
                    if (i < jump_table.length - 1) {
                        buf.append(", ");
                    }
                }
                buf.append(")");
                break;
            /* Lookup switch has variable length arguments.
             */
            case Const.LOOKUPSWITCH: {
                npairs = bytes.readInt();
                offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
                match = new int[npairs];
                jump_table = new int[npairs];
                default_offset += offset;
                buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append(
                        npairs).append(" (");
                for (int i = 0; i < npairs; i++) {
                    match[i] = bytes.readInt();
                    jump_table[i] = offset + bytes.readInt();
                    buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")");
                    if (i < npairs - 1) {
                        buf.append(", ");
                    }
                }
                buf.append(")");
            }
                break;
            /* Two address bytes + offset from start of byte stream form the
             * jump target
             */
            case Const.GOTO:
            case Const.IFEQ:
            case Const.IFGE:
            case Const.IFGT:
            case Const.IFLE:
            case Const.IFLT:
            case Const.JSR:
            case Const.IFNE:
            case Const.IFNONNULL:
            case Const.IFNULL:
            case Const.IF_ACMPEQ:
            case Const.IF_ACMPNE:
            case Const.IF_ICMPEQ:
            case Const.IF_ICMPGE:
            case Const.IF_ICMPGT:
            case Const.IF_ICMPLE:
            case Const.IF_ICMPLT:
            case Const.IF_ICMPNE:
                buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readShort());
                break;
            /* 32-bit wide jumps
             */
            case Const.GOTO_W:
            case Const.JSR_W:
                buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readInt());
                break;
            /* Index byte references local variable (register)
             */
            case Const.ALOAD:
            case Const.ASTORE:
            case Const.DLOAD:
            case Const.DSTORE:
            case Const.FLOAD:
            case Const.FSTORE:
            case Const.ILOAD:
            case Const.ISTORE:
            case Const.LLOAD:
            case Const.LSTORE:
            case Const.RET:
                if (wide) {
                    vindex = bytes.readUnsignedShort();
                    wide = false; // Clear flag
                } else {
                    vindex = bytes.readUnsignedByte();
                }
                buf.append("\t\t%").append(vindex);
                break;
            /*
             * Remember wide byte which is used to form a 16-bit address in the
             * following instruction. Relies on that the method is called again with
             * the following opcode.
             */
            case Const.WIDE:
                wide = true;
                buf.append("\t(wide)");
                break;
            /* Array of basic type.
             */
            case Const.NEWARRAY:
                buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">");
                break;
            /* Access object/class fields.
             */
            case Const.GETFIELD:
            case Const.GETSTATIC:
            case Const.PUTFIELD:
            case Const.PUTSTATIC:
                index = bytes.readUnsignedShort();
                buf.append("\t\t").append(
                        constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append(
                        verbose ? " (" + index + ")" : "");

                executeSql(
                    	"insert into method_depend values(?,?,?)",
                    	new Object[] {
                    		methodId,
                    		constant_pool.constantToString(index, Const.CONSTANT_Fieldref),
                    		opcode
                    	}
                    );
                break;
            /* Operands are references to classes in constant pool
             */
            case Const.NEW:
            case Const.CHECKCAST:
                buf.append("\t");
                //$FALL-THROUGH$
            case Const.INSTANCEOF:
                index = bytes.readUnsignedShort();
                buf.append("\t<").append(
                        constant_pool.constantToString(index, Const.CONSTANT_Class))
                        .append(">").append(verbose ? " (" + index + ")" : "");

                executeSql(
                    	"insert into method_depend values(?,?,?)",
                    	new Object[] {
                    		methodId,
                    		constant_pool.constantToString(index, Const.CONSTANT_Class),
                    		opcode
                    	}
                    );
                break;
            /* Operands are references to methods in constant pool
             */
            case Const.INVOKESPECIAL:
            case Const.INVOKESTATIC:
                index = bytes.readUnsignedShort();
                final Constant c = constant_pool.getConstant(index);
                // With Java8 operand may be either a CONSTANT_Methodref
                // or a CONSTANT_InterfaceMethodref.   (markro)
                buf.append("\t").append(
                        constant_pool.constantToString(index, c.getTag()))
                        .append(verbose ? " (" + index + ")" : "");
                executeSql(
                	"insert into method_depend values(?,?,?)",
                	new Object[] {
                		methodId,
                		constant_pool.constantToString(index, c.getTag()),
                		opcode
                	}
                );
                break;
            case Const.INVOKEVIRTUAL:
                index = bytes.readUnsignedShort();
                buf.append("\t").append(
                        constant_pool.constantToString(index, Const.CONSTANT_Methodref))
                        .append(verbose ? " (" + index + ")" : "");

                executeSql(
                    	"insert into method_depend values(?,?,?)",
                    	new Object[] {
                    		methodId,
                    		constant_pool.constantToString(index, Const.CONSTANT_Methodref),
                    		opcode
                    	}
                    );
                break;
            case Const.INVOKEINTERFACE:
                index = bytes.readUnsignedShort();
                final int nargs = bytes.readUnsignedByte(); // historical, redundant
                buf.append("\t").append(
                        constant_pool
                                .constantToString(index, Const.CONSTANT_InterfaceMethodref))
                        .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t")
                        .append(bytes.readUnsignedByte()); // Last byte is a reserved space
                executeSql(
                    	"insert into method_depend values(?,?,?)",
                    	new Object[] {
                    		methodId,
                    		constant_pool.constantToString(index, Const.CONSTANT_InterfaceMethodref),
                    		opcode
                    	}
                    );
                break;
            case Const.INVOKEDYNAMIC:
                index = bytes.readUnsignedShort();
                buf.append("\t").append(
                        constant_pool
                                .constantToString(index, Const.CONSTANT_InvokeDynamic))
                        .append(verbose ? " (" + index + ")\t" : "")
                        .append(bytes.readUnsignedByte())  // Thrid byte is a reserved space
                        .append(bytes.readUnsignedByte()); // Last byte is a reserved space

                executeSql(
                    	"insert into method_depend values(?,?,?)",
                    	new Object[] {
                    		methodId,
                    		constant_pool.constantToString(index, Const.CONSTANT_InvokeDynamic),
                    		opcode
                    	}
                    );
                break;
            /* Operands are references to items in constant pool
             */
            case Const.LDC_W:
            case Const.LDC2_W:
                index = bytes.readUnsignedShort();
                buf.append("\t\t").append(
                        constant_pool.constantToString(index, constant_pool.getConstant(index)
                                .getTag())).append(verbose ? " (" + index + ")" : "");
                break;
            case Const.LDC:
                index = bytes.readUnsignedByte();
                buf.append("\t\t").append(
                        constant_pool.constantToString(index, constant_pool.getConstant(index)
                                .getTag())).append(verbose ? " (" + index + ")" : "");
                break;
            /* Array of references.
             */
            case Const.ANEWARRAY:
                index = bytes.readUnsignedShort();
                buf.append("\t\t<").append(
                		Utility.compactClassName(constant_pool.getConstantString(index,
                                Const.CONSTANT_Class), false)).append(">").append(
                        verbose ? " (" + index + ")" : "");
                break;
            /* Multidimensional array of references.
             */
            case Const.MULTIANEWARRAY: {
                index = bytes.readUnsignedShort();
                final int dimensions = bytes.readUnsignedByte();
                buf.append("\t<").append(
                		Utility.compactClassName(constant_pool.getConstantString(index,
                                Const.CONSTANT_Class), false)).append(">\t").append(dimensions)
                        .append(verbose ? " (" + index + ")" : "");
            }
                break;
            /* Increment local variable.
             */
            case Const.IINC:
                if (wide) {
                    vindex = bytes.readUnsignedShort();
                    constant = bytes.readShort();
                    wide = false;
                } else {
                    vindex = bytes.readUnsignedByte();
                    constant = bytes.readByte();
                }
                buf.append("\t\t%").append(vindex).append("\t").append(constant);
                break;
            default:
                if (Const.getNoOfOperands(opcode) > 0) {
                    for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
                        buf.append("\t\t");
                        switch (Const.getOperandType(opcode, i)) {
                            case Const.T_BYTE:
                                buf.append(bytes.readByte());
                                break;
                            case Const.T_SHORT:
                                buf.append(bytes.readShort());
                                break;
                            case Const.T_INT:
                                buf.append(bytes.readInt());
                                break;
                            default: // Never reached
                                throw new IllegalStateException("Unreachable default case reached!");
                        }
                    }
                }
        }
    }

    private String getCode(org.apache.bcel.classfile.Method method) {
    	if (method.getCode() == null) {
    		return "";
    	}
    	return method.getCode().toString();
    }

}

Créer un diagramme de classes à partir du résultat de l'analyse de bcel

Un diagramme de classes est créé à partir de Sqlite qui stocke le résultat de l'analyse de bcel. plantUML.png

SqliteToGraph.java



package sqliteToGraph;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.bcel.Const;

import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;

public class SqliteToGraph {
	static final int MAX_MEMBER_SIZE = 10;

	public class ClassData {
		private int id;
		private String name;
		private String packageName;
		private String className;
		private int accessFlg;
		private String superClassName;

		public ClassData(ResultSet rs) throws SQLException {
			id = rs.getInt("id");
			name = rs.getString("name");
			accessFlg = rs.getInt("access_flags");
			superClassName = rs.getString("super_class_name");

			int ix = name.lastIndexOf(".");
			className = name.substring(ix + 1);
			packageName = name.substring(0, ix);
		}

		public int getId() {
			return id;
		}
		public String getName() {
			return name;
		}
		public String getPackageName() {
			return packageName;
		}
		public String getClassName() {
			return className;
		}
		public int getAccessFlg() {
			return accessFlg;
		}
		public String getSuperClassName() {
			return superClassName;
		}
	}

	public static void main(String[] args) throws SQLException, IOException{
		SqliteToGraph sg = new SqliteToGraph();
		String dbPath = "..\\bcelToSqlite\\output.sqlite";
		String path = "test_class.svg";
		sg.parse(dbPath, path);
		System.out.println("class:" + dbPath + "->" + path);
	}
	public void parse(String dbPath, String path) throws SQLException, IOException {
		Connection connection = null;
		ResultSet rs = null;
		PreparedStatement pstmt = null;
		PreparedStatement pstmtMethod = null;
		StringBuilder sb = new StringBuilder();
		try
		{
			// create a database connection
			// Connection connection = DriverManager.getConnection("jdbc:sqlite:C:/work/mydatabase.db");
			// Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
			connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
			pstmt = connection.prepareStatement("select id, name, access_flags, super_class_name from class order by id");
			rs = pstmt.executeQuery();
			HashMap<Integer, ClassData> mapClass = new HashMap<Integer, ClassData>();

			while(rs.next())
			{
				SqliteToGraph.ClassData data = new SqliteToGraph.ClassData(rs);
				mapClass.put(data.id, data);
			}
			rs.close();
			pstmt.close();
			pstmt = null;

			////
			sb.append("@startuml\n");
			sb.append("left to right direction\n");

			pstmt = connection.prepareStatement("select id, name , access_flag, type from field where class_id = ?");
			pstmtMethod = connection.prepareStatement("select distinct name , access_flag from method where class_id = ?");

			for(Integer key : mapClass.keySet()) {
				String prefix = "class";
				if ((mapClass.get(key).getAccessFlg() & Const.ACC_INTERFACE) == Const.ACC_INTERFACE) {
					prefix = "interface";
				}
				sb.append("  " + prefix +" \"" + mapClass.get(key).name + "\" {" + "\n");

				// field
				List<String> list = new ArrayList<String>();
				pstmt.setInt(1, mapClass.get(key).id);
				rs = pstmt.executeQuery();
				while(rs.next())
				{
					list.add(rs.getString("name"));
				}
				for(int i = 0; i < list.size()  ; ++i)  {
					sb.append("    " + list.get(i) + " \n");
					if (i > MAX_MEMBER_SIZE) {
						sb.append("    ... \n");
						break;
					}
				}
				rs.close();
				list = new ArrayList<String>();

				// method
				pstmtMethod.setInt(1, mapClass.get(key).id);
				rs = pstmtMethod.executeQuery();
				while(rs.next())
				{
					if ((rs.getInt("access_flag") & Const.ACC_PUBLIC) == Const.ACC_PUBLIC) {
						list.add(rs.getString("name"));
					}
				}
				rs.close();
				for(int i = 0; i < list.size()  ; ++i)  {
					sb.append("    " + list.get(i) + "() \n");
					if (i > MAX_MEMBER_SIZE) {
						sb.append("    ...() \n");
						break;
					}
				}
				sb.append("  }\n");
				if (checkSuperClassName(mapClass.get(key).getSuperClassName())) {
					sb.append("  " + mapClass.get(key).getSuperClassName() + " <|-- " + mapClass.get(key).name + "\n");
				}
			}
			pstmt.close();
			pstmt = null;

			pstmt = connection.prepareStatement("select class_id, interface_name from interface");
			rs = pstmt.executeQuery();
			while(rs.next())
			{
				if (checkSuperClassName(rs.getString("interface_name"))) {
					sb.append("  " + rs.getString("interface_name") + " <|.. " + mapClass.get(rs.getInt("class_id")).name + "\n");
				}
			}
			pstmt.close();
			pstmt = null;
			sb.append("@enduml\n");

			writeSvg(sb.toString(), path);
		}
		finally
		{
			try
			{
				if(rs != null)
				{
					rs.close();
				}
				if(pstmt != null)
				{
					pstmt.close();
				}
				if(pstmtMethod != null)
				{
					pstmtMethod.close();
				}
				if(connection != null)
				{
				connection.close();
				}
			}
			catch(SQLException e)
			{
				// connection close failed.
				System.err.println(e);
			}
		}

	}
    private boolean checkSuperClassName(String superClassName) {
		if (superClassName.startsWith("java.")) {
			return false;
		}
		if (superClassName.startsWith("javax.")) {
			return false;
		}
    	return true;
    }


	private static void writeSvg(String source, String path) throws IOException {
		SourceStringReader reader = new SourceStringReader(source);
		final ByteArrayOutputStream os = new ByteArrayOutputStream();
		// Write the first image to "os"
		@SuppressWarnings("deprecation")
		String desc = reader.generateImage(os, new FileFormatOption(FileFormat.SVG));
		os.close();

		final String svg = new String(os.toByteArray(), Charset.forName("UTF-8"));
		File out = new File(path);
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
		pw.write(svg);
		pw.close();

	}
}

Créer un graphe d'appel à partir du résultat de l'analyse de bcel

Un graphique d'appel de la méthode spécifiée est créé à partir de Sqlite qui stocke le résultat de l'analyse de bcel.

planguml2.png

DependMethod.java


package sqliteToGraph;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;

public class DependMethod {
	public static void main(String[] args) throws SQLException, IOException{
		DependMethod dm = new DependMethod();
		String dbPath = "..\\bcelToSqlite\\output.sqlite";
		String path = "test_depend.svg";
		String methodName = "org.apache.bcel.util.ClassPath.SYSTEM_CLASS_PATH";
		dm.parse(dbPath, path, methodName);
		System.out.println("depend:" + dbPath + "->" + path);
	}
	Connection connection = null;
	PreparedStatement pstmtLike = null;
	PreparedStatement pstmtEqual = null;

	class TreeItem {
		private String methodName;
		private List<TreeItem> children = new ArrayList<TreeItem>();
		public TreeItem(String m) {
			methodName = m;
		}
		public List<TreeItem> GetChildren() {
			return children;
		}
	}
	List<TreeItem> root = new ArrayList<TreeItem>();
	HashMap<String, TreeItem> map = new HashMap<String, TreeItem>();
	List<String> rectangles = new ArrayList<String>();

	public void parse(String dbPath, String path, String methodName) throws SQLException, IOException {
		ResultSet rs = null;
		try
		{
			connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
			pstmtLike = connection.prepareStatement("select  distinct method_depend.called_method from method inner join method_depend on method.id = method_depend.method_id where method_depend.called_method like ?");
			pstmtLike.setString(1, "%" + methodName + "%");
			rs = pstmtLike.executeQuery();


			while(rs.next())
			{
				TreeItem item = new TreeItem(rs.getString("called_method"));
				root.add(item);
				map.put(item.methodName, item);
			}
			rs.close();

			pstmtEqual = connection.prepareStatement("select  distinct method.fullname as call_method from method inner join method_depend on method.id = method_depend.method_id where method_depend.called_method like ?");
			for (TreeItem item : root) {
				walkDependency(item.methodName);
			}
			StringBuilder sbDef = new StringBuilder();
			StringBuilder sbArrow = new StringBuilder();
			StringBuilder sb = new StringBuilder();
			rectangles = new ArrayList<String>();

			sb.append("@startuml\n");
			for (TreeItem item : root) {
				drawDependency(item, sbDef, sbArrow);
			}
			sb.append(sbDef.toString());
			sb.append(sbArrow.toString());

			sb.append("@enduml\n");
			System.out.println(sb.toString());
			writeSvg(sb.toString() + "\n" + sb.toString(), path);

		}
		finally
		{
			try
			{
				if(rs != null)
				{
					rs.close();
				}
				if(pstmtLike != null)
				{
					pstmtLike.close();
				}
				if(pstmtEqual != null)
				{
					pstmtEqual.close();
				}
				if(connection != null)
				{
				connection.close();
				}
			}
			catch(SQLException e)
			{
				// connection close failed.
				System.err.println(e);
			}
		}

	}

	void walkDependency(String calledMethod) throws SQLException {
		pstmtEqual.setString(1, calledMethod);
		ResultSet rs = pstmtEqual.executeQuery();

		List<String> list = new ArrayList<String>();

		while(rs.next())
		{
			String callMethod = rs.getString("call_method");
			if (callMethod.equals(calledMethod)) {
				//Contre-mesures contre les appels de rappel
				continue;
			}
			list.add(callMethod);
			if (!map.containsKey(callMethod)) {
				TreeItem item = new TreeItem(callMethod);
				map.put(item.methodName, item);
				map.get(calledMethod).GetChildren().add(item);
			}
		}
		rs.close();

		for (String callMethod : list) {
			walkDependency(callMethod);
		}

	}
	void drawDependency(TreeItem item, StringBuilder sbDef, StringBuilder sbArrow) {
		if (!rectangles.contains(item.methodName)) {
			rectangles.add(item.methodName);
			sbDef.append("rectangle \"" + item.methodName + "\" as " + makeAlias(item.methodName) + "\n");
		}
		for (TreeItem child : item.GetChildren()) {
			sbArrow.append(makeAlias(item.methodName) + "<--" + makeAlias(child.methodName) + "\n");
			drawDependency(child, sbDef, sbArrow);
		}

	}
	private String makeAlias(String name) {
		name = name.replaceAll("/", "_");
		name = name.replaceAll(" ", "_");
		name = name.replaceAll("<", "_");
		name = name.replaceAll(">", "_");
		name = name.replaceAll("\\$", "_");
		name = name.replaceAll(";", "_");
		name = name.replaceAll("\\(", "_");
		name = name.replaceAll("\\)", "_");
		name = name.replaceAll("\\[", "_");
		name = name.replaceAll("\\]", "_");
		return name;
	}

	private static void writeSvg(String source, String path) throws IOException {
		SourceStringReader reader = new SourceStringReader(source);
		final ByteArrayOutputStream os = new ByteArrayOutputStream();
		// Write the first image to "os"
		@SuppressWarnings("deprecation")
		String desc = reader.generateImage(os, new FileFormatOption(FileFormat.SVG));
		os.close();

		final String svg = new String(os.toByteArray(), Charset.forName("UTF-8"));
		File out = new File(path);
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
		pw.write(svg);
		pw.close();

	}
}

Épilogue

Avec Doxygen et GraphViz, vous n'avez pas à faire cela. Doxygen peut également cracher du XML des résultats, donc je pense que c'est facile à analyser.

Cependant, étant donné que bcel est inclus dans des outils couramment utilisés tels que findbug, il peut être utilisé même lorsque vous appréciez l'interdiction d'Internet. À ce moment-là, je le faisais en le crachant dans du texte au lieu de Sqlite, puis en le collant dans Access.

De plus, le stockage des résultats de l'analyse dans la base de données est utile pour étudier les dépendances.

Recommended Posts

Exprimons le résultat de l'analyse du code d'octet Java dans un diagramme de classes
J'ai essayé d'exprimer les résultats avant et après de la classe Date avec une ligne droite numérique
Assurez-vous de comparer le résultat Java compareTo avec 0
Une histoire sur l'utilisation de l'API League Of Legends avec JAVA
Créons une application TODO en Java 5 Changer l'affichage de TODO
Obtenez le résultat de POST en Java
[Java] Obtenez la date avec la classe LocalDateTime
L'histoire de la création d'un lanceur de jeu avec une fonction de chargement automatique [Java]
Comment savoir quelle version Java d'un fichier de classe a été compilée
[Java] Comment accéder au début d'une chaîne spécifique à l'aide de la classe String
Faisons une application de calculatrice avec Java ~ Créez une zone d'affichage dans la fenêtre
[LeJOS] Contrôlons le moteur EV3 avec Java
Mesurer la taille d'un dossier avec Java
Calculer le score de similarité des chaînes de caractères avec JAVA
Incrémenté du troisième argument de la méthode iterate de la classe Stream ajoutée depuis Java9
Un examen rapide de Java appris en classe
Valider le jeton d'ID d'un utilisateur authentifié par AWS Cognito en Java
Première touche de la classe Files (ou Java 8)
Implémentons une fonction pour limiter le nombre d'accès à l'API avec SpringBoot + Redis
[Java] Découpez une partie de la chaîne de caractères avec Matcher et des expressions régulières
CI l'architecture des applications Java / Kotlin avec ArchUnit
Un examen rapide de Java appris en classe part4
L'histoire de la création d'un proxy inverse avec ProxyServlet
[Bases de Java] Créons un triangle avec une instruction for
Surveillez l'état interne des programmes Java avec Kubernetes
Vérifiez le comportement de Java Intrinsic Locks avec bpftrace
Faisons un robot! "Une simple démo de Java AWT Robot"
[Java] Obtenez la date 10 jours plus tard avec la classe Calendar
[Java] Lors de l'écriture du source ... Mémorandum ①
J'ai écrit un diagramme de séquence de l'exemple j.u.c.Flow
[LeJOS] Contrôlons à distance le moteur EV3 avec Java
Un examen rapide de Java appris en classe part3
Un examen rapide de Java appris en classe part2
Vérifiez le résultat de l'inférence de paramètre de type générique avec JShell
Une vue d'ensemble du framework Java natif de Kubernetes Quarkus
L'histoire de la création de DTO, semblable à Dao avec Java, SQLite
Récapitulez les éléments supplémentaires de la classe Optional dans Java 9
Remplacez seulement une partie de l'hôte URL par java
Raclons avec Java! !!
L'histoire de la création d'une version Java du serveur Minecraft avec GCP (et également de la création d'une liste blanche)
Histoire de créer une application de gestion de tâches avec Swing, Java
Trouvez la classe d'adresse et le type d'adresse à partir de l'adresse IP avec Java
Une explication rapide des cinq types de statique Java
Faisons une application de calcul avec Java ~ Afficher la fenêtre de l'application
Une histoire remplie des bases de Spring Boot (résolu)
Vérifier le fonctionnement de deux rôles avec une application de chat
Jetons un coup d'œil à l'écran de Quant Analyzer!
[Java] Simplifiez la mise en œuvre de la gestion de l'historique des données avec Reladomo
Implémentation d'un analyseur de syntaxe mathématique par méthode d'analyse syntaxique descendante récursive (Java)
[Java] Remplaçons les objets de données par un mappeur ~ BeanMapper Orika ~
Comment déplacer une autre classe avec une action de bouton d'une autre classe.
Expliquez les mérites du modèle d'État avec le jugement de notation du film
Réécrire les affectations de langage C de l'université avec Node.js
À propos du comportement lors de la création d'un mappage de fichiers avec Java
Trouvez le nombre de jours dans un mois avec Kotlin
Représentez graphiquement les informations du capteur de Raspberry Pi en Java et vérifiez-les avec un navigateur Web
Expérimentons l'expansion en ligne Java
Exploitons Excel avec Java! !!
[Java] Comparateur de la classe Collection