Lassen Sie uns das Ergebnis der Analyse von Java-Bytecode in einem Klassendiagramm ausdrücken

Drücken Sie das Ergebnis der Analyse von Java-Bytecode mit einem Klassendiagramm oder einem Aufrufdiagramm aus

Zweck

Extrahieren Sie Klassen- und Methodeninformationen aus dem Java-Bytecode und zeichnen Sie sie in SQLite auf. Beschreiben Sie das Klassendiagramm und das Aufrufdiagramm anhand der oben aufgezeichneten Informationen.

Wenn Sie doxygen + graphviz verwenden können, sollten Sie es verwenden.

Nutzungsumgebung

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

Erläuterung der verwendeten Bibliothek

bcel Die BCEL-API (Byte Code Engineering Library) ist ein Toolkit für die statische Analyse und die dynamische Erstellung von Java-Klassendateien. FindBug verwendet BCEL, um statische Analysen aus Klassendateien durchzuführen.

bcel.png

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

Beispielcode

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

Der folgende codeToString ist außerdem hilfreich für die Interpretation der Bytecode-Binärdatei. https://github.com/llmhyy/commons-bcel/blob/master/src/main/java/org/apache/bcel/classfile/Utility.java

Sqlite-JDBC Sie können es von der folgenden Seite mit JDBC herunterladen, das SQLite betreibt.

herunterladen https://bitbucket.org/xerial/sqlite-jdbc/downloads/

Beispielcode

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);
			}
		}
	}
}

Sie können auch benutzerdefinierte Funktionen erstellen. Darüber hinaus ist der folgende Testcode für SQLite-spezifische Vorgänge hilfreich. https://github.com/xerial/sqlite-jdbc/tree/c7c5604bcc584460268abc9a64df2953fca788d3/src/test/java/org/sqlite

PlantUML Sie können die folgende Abbildung einschließlich UML in einer Sprache namens DSL schreiben.

Sie können die aktuelle Beschreibung offiziell auf der Webseite ausprobieren. Es gibt auch ein Redmine-Plug-In, sodass Sie verschiedene Abbildungen als Textdaten in das Wiki einbetten können.

offiziell: http://plantuml.com/ja/

herunterladen http://plantuml.com/ja/download

Stichprobe

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

Sie können PNG und SVG auch aus Ihrem eigenen Programm ausgeben, indem Sie die Datei plantuml.jar in Ihr Java-Projekt integrieren. http://plantuml.com/ja/api

Versuchsergebnis

https://github.com/mima3/BcelToSqlite

Nehmen Sie Sqlite mit bcel auf

Der folgende Code gibt eine Jar-Datei an, analysiert die darin gespeicherte Klassendatei und zeichnet Klassen-, Methoden- und Feldinformationen in SQLite auf.

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;

	/**
	 *Implementiert unter Bezugnahme auf Folgendes
	 * 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()
    		}
    	);

    	//Methode abrufen
        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);
        }

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

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

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

        //verpflichten
        ++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());
			}
		}


        //Anmerkung
        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()
    			}
    	);

        //Anmerkung
        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.
     */

    /**
     *Als Referenz unten implementiert
     * 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();
    }

}

Erstellen Sie ein Klassendiagramm aus dem Analyseergebnis von bcel

Aus Sqlite wird ein Klassendiagramm erstellt, in dem das Analyseergebnis von bcel gespeichert wird. 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();

	}
}

Erstellen Sie ein Anrufdiagramm aus dem Analyseergebnis von bcel

Aus Sqlite wird ein Aufrufdiagramm der angegebenen Methode erstellt, in dem das Analyseergebnis von bcel gespeichert wird.

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)) {
				//Gegenmaßnahmen gegen Rückrufaktionen
				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();

	}
}

Nachwort

Mit Doxygen und GraphViz müssen Sie dies nicht tun. Doxygen kann auch XML der Ergebnisse ausspucken, daher denke ich, dass es einfach zu analysieren ist.

Da bcel jedoch in häufig verwendeten Tools wie findbug enthalten ist, kann es auch dort verwendet werden, wo Sie das Internetverbot genießen. Zu diesem Zeitpunkt habe ich es getan, indem ich es anstelle von Sqlite in Text ausgespuckt und dann in Access gesteckt habe.

Das Speichern der Analyseergebnisse in der Datenbank ist auch nützlich, um Abhängigkeiten zu untersuchen.

Recommended Posts

Lassen Sie uns das Ergebnis der Analyse von Java-Bytecode in einem Klassendiagramm ausdrücken
Ich habe versucht, die Ergebnisse vor und nach der Date-Klasse mit einer geraden Zahl auszudrücken
Stellen Sie sicher, dass Sie das Java compareTo-Ergebnis mit 0 vergleichen
Eine Geschichte über das Erreichen der League Of Legends-API mit JAVA
Lassen Sie uns eine TODO-App in Java 5 erstellen. Schalten Sie die Anzeige von TODO um
Holen Sie sich das Ergebnis von POST in Java
[Java] Ermitteln Sie das Datum mit der LocalDateTime-Klasse
Die Geschichte eines Game Launcher mit automatischer Ladefunktion [Java]
So finden Sie heraus, welche Java-Version der Klassendatei kompiliert wurde
[Java] Wie man mit der String-Klasse an die Spitze eines bestimmten Strings kommt
Erstellen wir eine Taschenrechner-App mit Java ~ Erstellen Sie einen Anzeigebereich im Fenster
[LeJOS] Lassen Sie uns den EV3-Motor mit Java steuern
Messen Sie die Größe eines Ordners mit Java
Berechnen Sie die Ähnlichkeitsbewertung von Zeichenketten mit JAVA
Inkrementiert durch das dritte Argument der iterierten Methode der aus Java9 hinzugefügten Stream-Klasse
Ein kurzer Überblick über Java, das im Unterricht gelernt wurde
Überprüfen Sie das ID-Token eines von AWS Cognito in Java authentifizierten Benutzers
Erster Touch der Files-Klasse (oder Java 8)
Implementieren wir eine Funktion, um die Anzahl der Zugriffe auf die API mit SpringBoot + Redis zu begrenzen
[Java] Schneiden Sie einen Teil der Zeichenfolge mit Matcher und regulären Ausdrücken aus
CI die Architektur von Java / Kotlin-Anwendungen mit ArchUnit
Ein kurzer Überblick über Java, das in Klasse 4 gelernt wurde
Die Geschichte, einen Reverse-Proxy mit ProxyServlet zu erstellen
[Java-Grundlagen] Lassen Sie uns ein Dreieck mit einer for-Anweisung erstellen
Überwachen Sie den internen Status von Java-Programmen mit Kubernetes
Überprüfen Sie das Verhalten von Java Intrinsic Locks mit bpftrace
Lass uns einen Roboter bauen! "Eine einfache Demo von Java AWT Robot"
[Java] Holen Sie sich das Datum 10 Tage später mit der Calendar-Klasse
[Java] Beim Schreiben der Quelle ... Memorandum ①
Ich habe ein Sequenzdiagramm des j.u.c.Flow-Beispiels geschrieben
[LeJOS] Lassen Sie uns den EV3-Motor mit Java fernsteuern
Ein kurzer Überblick über Java, das in Klasse 3 gelernt wurde
Ein kurzer Überblick über Java, das in Klasse 2 gelernt wurde
Überprüfen Sie das Ergebnis der generischen Parameterinferenz mit JShell
Eine Übersicht über das native Java-Framework Quarkus von Kubernetes
Die Geschichte von dto, dao-like mit Java, SQLite
Fassen Sie die zusätzlichen Elemente der optionalen Klasse in Java 9 zusammen
Ersetzen Sie nur einen Teil des URL-Hosts durch Java
Lass uns mit Java kratzen! !!
Die Geschichte des Erstellens einer Java-Version des Minecraft-Servers mit GCP (und dem Festlegen einer weißen Liste)
Die Geschichte der Erstellung einer Task-Management-Anwendung mit Swing, Java
Suchen Sie die Adressklasse und den Adresstyp aus der IP-Adresse mit Java
Eine kurze Erklärung der fünf Arten von Java Static
Erstellen wir eine Taschenrechner-App mit Java ~ Zeigen Sie das Anwendungsfenster an
Eine Geschichte voller Grundlagen von Spring Boot (gelöst)
Überprüfen Sie die Funktion von zwei Rollen mit einer Chat-Anwendung
Werfen wir einen Blick auf den Bildschirm von Quant Analyzer!
[Java] Vereinfachen Sie die Implementierung der Datenverlaufsverwaltung mit Reladomo
Implementierung eines mathematischen Syntaxanalysators durch rekursive absteigende Syntaxanalysemethode (Java)
[Java] Ersetzen wir Datenobjekte durch einen Mapper ~ BeanMapper Orika ~
So verschieben Sie eine andere Klasse mit einer Schaltflächenaktion einer anderen Klasse.
Erläutern Sie die Vorzüge des staatlichen Musters anhand des Bewertungsurteils des Films
Schreiben Sie die Zuweisungen der Universität C mit Node.js neu
Informationen zum Verhalten beim Erstellen einer Dateizuordnung mit Java
Finden Sie mit Kotlin die Anzahl der Tage in einem Monat
Stellen Sie die Sensorinformationen von Raspberry Pi in Java grafisch dar und überprüfen Sie sie mit einem Webbrowser
Experimentieren wir mit der Java-Inline-Erweiterung
Lassen Sie uns Excel mit Java betreiben! !!
[Java] Komparator der Collection-Klasse