1 package jp.igapyon.jcfa;
\r
3 import java.io.ByteArrayOutputStream;
\r
5 import java.io.IOException;
\r
7 import jp.igapyon.jcfa.util.JcfaUtil;
\r
8 import jp.igapyon.jcfa.util.JcfaWriteUtil;
\r
9 import jp.igapyon.jcfa.vo.JcfaClass;
\r
10 import jp.igapyon.jcfa.vo.JcfaCode;
\r
11 import jp.igapyon.jcfa.vo.JcfaField;
\r
12 import jp.igapyon.jcfa.vo.JcfaMethod;
\r
13 import jp.igapyon.jcfa.vo.JcfaUnit;
\r
15 import org.apache.bcel.Constants;
\r
16 import org.apache.bcel.classfile.ClassFormatException;
\r
17 import org.apache.bcel.classfile.ClassParser;
\r
18 import org.apache.bcel.classfile.Code;
\r
19 import org.apache.bcel.classfile.ConstantValue;
\r
20 import org.apache.bcel.classfile.Field;
\r
21 import org.apache.bcel.classfile.JavaClass;
\r
22 import org.apache.bcel.classfile.Method;
\r
23 import org.apache.bcel.generic.Type;
\r
25 public class JavaClassFileAnalyzer {
\r
26 protected JcfaUnit jcfaUnit = new JcfaUnit();
\r
28 public static final void main(final String[] args) {
\r
29 new JavaClassFileAnalyzer().parseDir(new File("./bin/test"));
\r
30 new JavaClassFileAnalyzer().parseDir(new File(
\r
31 "./bin/jp/igapyon/jcfa/vo"));
\r
34 private void parseDir(final File dir) {
\r
35 final File[] files = dir.listFiles();
\r
36 if (files == null) {
\r
39 for (File file : files) {
\r
40 if (file.isDirectory()) {
\r
43 if (file.isFile()) {
\r
44 if (file.getName().endsWith(".class")) {
\r
45 new JavaClassFileAnalyzer().process(file, new File(
\r
46 "./testJavaClass/output"));
\r
52 private final void process(final File inputFile, final File outputDir) {
\r
54 final JavaClass jc = new ClassParser(inputFile.getCanonicalPath())
\r
56 final JcfaClass jcfaClass = new JcfaClass();
\r
57 jcfaUnit.getClassList().add(jcfaClass);
\r
59 jcfaClass.setName(jc.getClassName());
\r
60 jcfaClass.setExtendsName(jc.getSuperclassName());
\r
62 jcfaClass.getComment().getCommentList()
\r
63 .add("TODO import func. is missing.");
\r
64 jcfaClass.getComment().setJavaDoc(true);
\r
66 final String[] split = jc.getClassName().split("\\.");
\r
67 File actualyTargetDir = outputDir;
\r
68 if (split.length > 1) {
\r
69 for (int index = 0; index < split.length - 1; index++) {
\r
70 actualyTargetDir = new File(actualyTargetDir, split[index]);
\r
71 actualyTargetDir.mkdirs();
\r
75 analyzeFields(jc, jcfaClass);
\r
76 analyzeMethods(jc, jcfaClass);
\r
78 jcfaUnit.setTargetFile(new File(actualyTargetDir,
\r
79 split[split.length - 1] + ".jcfa"));
\r
81 JcfaWriteUtil.writeToFile(jcfaUnit);
\r
82 } catch (ClassFormatException e) {
\r
83 e.printStackTrace();
\r
84 } catch (IOException e) {
\r
85 e.printStackTrace();
\r
89 private void analyzeFields(final JavaClass jc, final JcfaClass jcfaClass) {
\r
90 final org.apache.bcel.classfile.Field[] fields = jc.getFields();
\r
91 for (int indexField = 0; indexField < fields.length; indexField++) {
\r
92 final Field field = fields[indexField];
\r
93 analyzeField(jc, field, jcfaClass);
\r
97 private void analyzeField(final JavaClass jc, final Field field,
\r
98 final JcfaClass jcfaClass) {
\r
99 final JcfaField jcfaField = new JcfaField();
\r
100 jcfaField.setName(field.getName());
\r
101 jcfaClass.getFieldList().add(jcfaField);
\r
103 jcfaField.getComment().setJavaDoc(true);
\r
105 // TODO type should be more collect.
\r
106 jcfaField.setType(field.getType().toString());
\r
108 String access = "";
\r
109 access += field.isPublic() ? "public " : "";
\r
110 access += field.isProtected() ? "protected " : "";
\r
111 access += field.isPrivate() ? "private " : "";
\r
112 access += field.isAbstract() ? "abstract " : "";
\r
113 access += field.isStatic() ? "static " : "";
\r
114 access += field.isVolatile() ? "volatile " : "";
\r
115 access += field.isFinal() ? "final " : "";
\r
116 jcfaField.setAccess(access);
\r
118 final ConstantValue cv = field.getConstantValue();
\r
120 jcfaField.setConstantValue("\""
\r
121 + jc.getConstantPool().getConstantString(
\r
122 cv.getConstantValueIndex(),
\r
123 Constants.CONSTANT_String) + "\"");
\r
128 .add("FIXME other type support is missing. <br />Now only String.");
\r
132 private void analyzeMethods(final JavaClass jc, final JcfaClass jcfaClass)
\r
133 throws IOException {
\r
134 final org.apache.bcel.classfile.Method[] methods = jc.getMethods();
\r
135 for (int indexMethod = 0; indexMethod < methods.length; indexMethod++) {
\r
136 final Method method = methods[indexMethod];
\r
137 analyzeMethod(jc, method, jcfaClass);
\r
148 * @throws IOException
\r
150 private void analyzeMethod(final JavaClass jc, final Method method,
\r
151 final JcfaClass jcfaClass) throws IOException {
\r
152 final JcfaMethod jcfaMethod = new JcfaMethod();
\r
153 jcfaClass.getMethodList().add(jcfaMethod);
\r
155 jcfaMethod.setName(method.getName());
\r
156 jcfaMethod.getComment().setJavaDoc(true);
\r
157 if (jcfaMethod.getName().equals("<init>")) {
\r
158 jcfaMethod.getComment().getCommentList()
\r
159 .add("Default constructor.");
\r
161 jcfaMethod.getComment().getCommentList().add("Method.");
\r
164 for (Type type : method.getArgumentTypes()) {
\r
165 jcfaMethod.getComment().getCommentList().add(type.toString());
\r
166 jcfaMethod.getArugumentTypeList().add(type.toString());
\r
168 jcfaMethod.setType(method.getReturnType().toString());
\r
170 final Code code = method.getCode();
\r
171 if (code == null) {
\r
175 final byte[] codes = code.getCode();
\r
176 for (int pc = 0; pc < codes.length; pc++) {
\r
177 final int operands = analyzeCodes(jc, method, jcfaClass,
\r
178 jcfaMethod, pc, codes);
\r
179 if (operands < 0) {
\r
187 private int analyzeCodes(final JavaClass jc, final Method method,
\r
188 final JcfaClass jcfaClass, final JcfaMethod jcfaMethod,
\r
189 final int pc, final byte[] codes) throws IOException {
\r
190 final JcfaCode jcfaCode = new JcfaCode();
\r
191 jcfaMethod.getCodeList().add(jcfaCode);
\r
192 jcfaCode.setJavaClass(jc);
\r
194 jcfaCode.setOpcode(JcfaUtil.byte2UnsignedByte(codes[pc]));
\r
195 jcfaCode.getComment()
\r
197 .add("" + pc + ": "
\r
198 + Constants.OPCODE_NAMES[jcfaCode.getOpcode()]);
\r
200 short operands = Constants.NO_OF_OPERANDS[jcfaCode.getOpcode()];
\r
201 if (operands < 0) {
\r
202 jcfaCode.getComment()
\r
204 .add("FIXME NO_OF_OPERANDS has negative value:"
\r
205 + Constants.OPCODE_NAMES[jcfaCode.getOpcode()]
\r
206 + ": " + operands);
\r
210 switch (jcfaCode.getOpcode()) {
\r
211 case Constants.RETURN:
\r
213 case Constants.GETSTATIC:
\r
215 case Constants.LDC:
\r
217 case Constants.INVOKEVIRTUAL:
\r
218 case Constants.INVOKESPECIAL:
\r
220 case Constants.LOOKUPSWITCH: {
\r
221 int result = JcfaUtil.byte2Int(codes[pc + 1], codes[pc + 2],
\r
222 codes[pc + 3], codes[pc + 4]);
\r
223 System.out.println(" TODO skipping bytes: " + (result));
\r
225 int lookupOp = pc + 5;
\r
227 short diff = JcfaUtil.byte2UnsignedByte(codes[lookupOp++]);
\r
228 System.out.println(" TODO skipping bytes: " + (diff));
\r
230 int loopCount = JcfaUtil.byte2Int(codes[lookupOp++],
\r
231 codes[lookupOp++], codes[lookupOp++], codes[lookupOp++]);
\r
233 short diff2 = JcfaUtil.byte2UnsignedByte(codes[lookupOp++]);
\r
234 jcfaCode.getComment().getCommentList()
\r
235 .add(" TODO skipping bytes: " + (diff2));
\r
237 operands += (lookupOp - pc);
\r
245 final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
\r
246 outStream.write(codes, pc, operands + 1);
\r
248 jcfaCode.setCodes(outStream.toByteArray());
\r