diff --git a/JFunc/build.gradle b/JFunc/build.gradle index 031dc65..74dff9e 100644 --- a/JFunc/build.gradle +++ b/JFunc/build.gradle @@ -20,8 +20,9 @@ dependencies { compile group: "com.fasterxml.jackson.core", name: "jackson-databind", version: "2.5.4" compile 'org.ow2.asm:asm-all:5.1' compile 'com.github.javaparser:javaparser-core:2.3.0' - compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.12' + compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.12' compile group: 'org.slf4j', name: 'slf4j-log4j12', version:'1.7.12' + compile group: 'commons-io', name: 'commons-io', version:'2.5' testCompile group: 'junit', name: 'junit', version: '4.+' diff --git a/JFunc/src/main/java/com/jfunc/asm/AppMetadata.java b/JFunc/src/main/java/com/jfunc/asm/AppMetadata.java new file mode 100644 index 0000000..da97493 --- /dev/null +++ b/JFunc/src/main/java/com/jfunc/asm/AppMetadata.java @@ -0,0 +1,14 @@ +package com.jfunc.asm; + +import java.util.ArrayList; +import java.util.List; + +public class AppMetadata { + + private final List classMetaData = new ArrayList(); + + public List getClassMetaData() { + return classMetaData; + } + +} diff --git a/JFunc/src/main/java/com/jfunc/asm/ClassMetadata.java b/JFunc/src/main/java/com/jfunc/asm/ClassMetadata.java new file mode 100644 index 0000000..746f12a --- /dev/null +++ b/JFunc/src/main/java/com/jfunc/asm/ClassMetadata.java @@ -0,0 +1,46 @@ +package com.jfunc.asm; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public class ClassMetadata { + + private final String className; + + private final ClassNode classNode; + + private final List methodMetadataList = new ArrayList(); + + private final List fieldNodes = new ArrayList(); + + public ClassMetadata(ClassNode classNode) { + this.className = classNode.sourceFile; + this.classNode = classNode; + List methodnodes = classNode.methods; + for (MethodNode methodNode : methodnodes) { + this.getMethodMetadataList().add(new MethodMetaData(methodNode, classNode)); + } + this.fieldNodes.addAll(classNode.fields); + } + + public String getClassName() { + return className; + } + + public ClassNode getClassNode() { + return classNode; + } + + public List getMethodMetadataList() { + return methodMetadataList; + } + + public List getFieldNodes() { + return fieldNodes; + } + +} diff --git a/JFunc/src/main/java/com/jfunc/asm/MethodMetaData.java b/JFunc/src/main/java/com/jfunc/asm/MethodMetaData.java index fb2ed2a..e2222fb 100644 --- a/JFunc/src/main/java/com/jfunc/asm/MethodMetaData.java +++ b/JFunc/src/main/java/com/jfunc/asm/MethodMetaData.java @@ -10,6 +10,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.LineNumberNode; @@ -21,83 +22,89 @@ public class MethodMetaData { - private static Printer printer = new Textifier(); - private static TraceMethodVisitor mp = new TraceMethodVisitor(printer); - - private final MethodNode methodNode; - private final List byteCode; - private final String returnType; - private final Map> lineToOperationsMap; - private final List internallyCalledMethods = new ArrayList<>(); - private final List internallyRefferedFields = new ArrayList<>(); - - public MethodMetaData(MethodNode methodNode) { - this.methodNode = methodNode; - this.byteCode = generateByteCodeOfMethod(methodNode); - this.returnType = Type.getReturnType(methodNode.desc).getClassName(); - this.lineToOperationsMap = getLineToOperationsMap(methodNode.instructions); - } - - public String getMethodNode() { - return this.methodNode.name; - } - - public String getMethodReturnType() { - return this.returnType; - } - - public List getByteCode() { - return Collections.unmodifiableList(this.byteCode); - } - - public Map> getLineToOperationsMap() { - return Collections.unmodifiableMap(this.lineToOperationsMap); - } - - public List getInternallyCalledMethods() { - return Collections.unmodifiableList(internallyCalledMethods); - } - - public List getInternallyRefferedFields() { - return Collections.unmodifiableList(internallyRefferedFields); - } - - private List generateByteCodeOfMethod(MethodNode methodNode) { - List byteCodeList = new ArrayList<>(); - InsnList inList = methodNode.instructions; - for (int i = 0; i < inList.size(); i++) { - byteCodeList.add(insnToString(inList.get(i))); - } - return byteCodeList; - } - - private String insnToString(AbstractInsnNode insn) { - insn.accept(mp); - StringWriter sw = new StringWriter(); - printer.print(new PrintWriter(sw)); - printer.getText().clear(); - return sw.toString(); - } - - private Map> getLineToOperationsMap(InsnList insList) { - Map> lineToOperationsMap = new LinkedHashMap<>(); - LineNumberNode key = null; - for (int i = 0; i < insList.size(); i++) { - if (insList.get(i) instanceof LineNumberNode) { - key = (LineNumberNode) insList.get(i); - lineToOperationsMap.put(key, new ArrayList()); - } else { - if (key != null && lineToOperationsMap.containsKey(key)) { - lineToOperationsMap.get(key).add(insList.get(i)); - if (insList.get(i) instanceof FieldInsnNode) { - internallyRefferedFields.add(new InternalFeild((FieldInsnNode) insList.get(i), key)); - } - if (insList.get(i) instanceof MethodInsnNode) { - internallyCalledMethods.add(new InternalMethod((MethodInsnNode) insList.get(i), key)); - } - } - } - } - return lineToOperationsMap; - } + private static Printer printer = new Textifier(); + private static TraceMethodVisitor mp = new TraceMethodVisitor(printer); + + private final String className; + private final MethodNode methodNode; + private final List byteCode; + private final String returnType; + private final Map> lineToOperationsMap; + private final List internallyCalledMethods = new ArrayList<>(); + private final List internallyRefferedFields = new ArrayList<>(); + + public MethodMetaData(MethodNode methodNode, ClassNode classNode) { + this.className = classNode.name; + this.methodNode = methodNode; + this.byteCode = generateByteCodeOfMethod(methodNode); + this.returnType = Type.getReturnType(methodNode.desc).getClassName(); + this.lineToOperationsMap = getLineToOperationsMap(methodNode.instructions); + } + + public String getClassName() { + return className; + } + + public MethodNode getMethodNode() { + return this.methodNode; + } + + public String getMethodReturnType() { + return this.returnType; + } + + public List getByteCode() { + return Collections.unmodifiableList(this.byteCode); + } + + public Map> getLineToOperationsMap() { + return Collections.unmodifiableMap(this.lineToOperationsMap); + } + + public List getInternallyCalledMethods() { + return Collections.unmodifiableList(internallyCalledMethods); + } + + public List getInternallyRefferedFields() { + return Collections.unmodifiableList(internallyRefferedFields); + } + + private List generateByteCodeOfMethod(MethodNode methodNode) { + List byteCodeList = new ArrayList<>(); + InsnList inList = methodNode.instructions; + for (int i = 0; i < inList.size(); i++) { + byteCodeList.add(insnToString(inList.get(i))); + } + return byteCodeList; + } + + private String insnToString(AbstractInsnNode insn) { + insn.accept(mp); + StringWriter sw = new StringWriter(); + printer.print(new PrintWriter(sw)); + printer.getText().clear(); + return sw.toString(); + } + + private Map> getLineToOperationsMap(InsnList insList) { + Map> lineToOperationsMap = new LinkedHashMap<>(); + LineNumberNode key = null; + for (int i = 0; i < insList.size(); i++) { + if (insList.get(i) instanceof LineNumberNode) { + key = (LineNumberNode) insList.get(i); + lineToOperationsMap.put(key, new ArrayList()); + } else { + if (key != null && lineToOperationsMap.containsKey(key)) { + lineToOperationsMap.get(key).add(insList.get(i)); + if (insList.get(i) instanceof FieldInsnNode) { + internallyRefferedFields.add(new InternalFeild((FieldInsnNode) insList.get(i), key)); + } + if (insList.get(i) instanceof MethodInsnNode) { + internallyCalledMethods.add(new InternalMethod((MethodInsnNode) insList.get(i), key)); + } + } + } + } + return lineToOperationsMap; + } } diff --git a/JFunc/src/main/java/com/jfunc/util/FieldUtil.java b/JFunc/src/main/java/com/jfunc/util/FieldUtil.java index b1bada4..2371a09 100644 --- a/JFunc/src/main/java/com/jfunc/util/FieldUtil.java +++ b/JFunc/src/main/java/com/jfunc/util/FieldUtil.java @@ -30,5 +30,11 @@ public static ObjectNode getReasonsForLogStatements(InternalFeild internalField) resonsNode.put(JfuncConstants.LINENUMBER + internalField.getLineNumber(), JfuncConstants.LOGGER); return resonsNode; } - + + public static ObjectNode getReasonsForGlobalFields(InternalFeild internalField) { + ObjectMapper mapper = JsonUtils.getObjectMapper(); + ObjectNode resonsNode = mapper.createObjectNode(); + resonsNode.put(JfuncConstants.LINENUMBER + internalField.getLineNumber(), JfuncConstants.LOGGER); + return resonsNode; + } } diff --git a/JFunc/src/main/java/com/jfunc/validator/FunctionValidator.java b/JFunc/src/main/java/com/jfunc/validator/FunctionValidator.java index 57ba80a..bc9385b 100644 --- a/JFunc/src/main/java/com/jfunc/validator/FunctionValidator.java +++ b/JFunc/src/main/java/com/jfunc/validator/FunctionValidator.java @@ -5,16 +5,26 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jfunc.asm.AppMetadata; +import com.jfunc.asm.ClassMetadata; import com.jfunc.asm.InternalFeild; import com.jfunc.asm.MethodMetaData; import com.jfunc.core.Validator; @@ -25,92 +35,197 @@ public class FunctionValidator implements Validator { - private final String filePath; - private File classFile; - - private ClassReader classReader = null; - private ClassNode classNode = new ClassNode(); - private List methodNodes; - - public FunctionValidator(String path) throws FileNotFoundException { - this.filePath = path; - File classFile = new File(filePath); - if (FileUtil.isFileExists(classFile)) { - this.classFile = classFile; - init(this.classFile); - } else { - throw new FileNotFoundException(); - } - } - - @SuppressWarnings("unchecked") - private void init(File classFile) { - try (InputStream fileStream = new FileInputStream(classFile)) { - classReader = new ClassReader(fileStream); - } catch (IOException e) { - e.printStackTrace(); - } - classReader.accept(classNode, 0); - methodNodes = classNode.methods; - } - - @Override - public String validate(String methodName) { - return validate(methodName, false, false); - } - - @Override - public String validate(String methodName, boolean skipLogStatements, boolean skipPrintStatements) { - for (MethodNode methodNode : methodNodes) { - if (StringUtils.equals(methodName, methodNode.name)) { - return getReasons(methodNode, skipLogStatements, skipPrintStatements).toString(); - } - } - return "No method found with name" + methodName; - } - - // At present we are checking for 1) Void 2) Print-Statements 3) Log-Statements - private ObjectNode getReasons(MethodNode methodNode, boolean skipLogStatements, boolean skipPrintStatements) { - ObjectMapper mapper = JsonUtils.getObjectMapper(); - ObjectNode objectNode = mapper.createObjectNode(); - MethodMetaData methodMetaData = new MethodMetaData(methodNode); - - // First check whether method type is "Void" - if (VoidTypeUtil.isVoid(methodMetaData)) { - objectNode.put(JfuncConstants.ISFUNCTIONAL, false); - objectNode.set(JfuncConstants.REASONS, VoidTypeUtil.getReasons(methodMetaData)); - return objectNode; - } - - // Check for any "Print" and "Log" statements in a method - if (!(skipPrintStatements && skipLogStatements)) { - ArrayNode reasonObjectNode = mapper.createArrayNode(); - boolean isFunctionSet = false; - List internalFields = methodMetaData.getInternallyRefferedFields(); - for (InternalFeild internalField : internalFields) { - if (!skipPrintStatements && FieldUtil.containsPrintStatements(internalField)) { - if (!isFunctionSet) { - objectNode.put(JfuncConstants.ISFUNCTIONAL, false); - isFunctionSet = true; - } - reasonObjectNode.add(FieldUtil.getReasonsForPrintStatements(internalField)); - } - if (!skipLogStatements && FieldUtil.containsLogStatements(internalField)) { - if (!isFunctionSet) { - objectNode.put(JfuncConstants.ISFUNCTIONAL, false); - isFunctionSet = true; - } - reasonObjectNode.add(FieldUtil.getReasonsForLogStatements(internalField)); - } - } - if (!isFunctionSet) { - objectNode.put(JfuncConstants.ISFUNCTIONAL, true); - } else { - objectNode.set(JfuncConstants.REASONS, reasonObjectNode); - } - } - return objectNode; - } + private final String filePath; + private final Collection classFiles; + private ClassReader classReader = null; + private ClassNode classNode; + private final List fieldNodes = new ArrayList(); + private final AppMetadata appNode = new AppMetadata(); + private List results = new ArrayList(); + + private final boolean skipLogStatements = false; + private final boolean skipPrintStatements = false; + + public FunctionValidator(String path) throws FileNotFoundException { + this.filePath = path; + classFiles = new ArrayList(); + File file = new File(filePath); + if (file.isDirectory()) { + classFiles.addAll(FileUtils.listFiles(file, JfuncConstants.EXTENSIONS, true)); + } else if (FileUtil.isFileExists(file)) { + classFiles.add(file); + } else { + throw new FileNotFoundException(); + } + + for (File classFile : classFiles) { + init(classFile); + } + } + + private void init(File classFile) { + + try (InputStream fileStream = new FileInputStream(classFile)) { + classReader = new ClassReader(fileStream); + } catch (IOException e) { + e.printStackTrace(); + } + classNode = new ClassNode(); + classReader.accept(classNode, ClassReader.EXPAND_FRAMES); + appNode.getClassMetaData().add(new ClassMetadata(classNode)); + } + + private Set getNonFinalFieldNodes() { + return fieldNodes.stream().parallel().filter( + field -> (Type.getType(field.desc).getSort() != Type.OBJECT && field.access != Opcodes.ACC_FINAL) + || (Type.getType(field.desc).getSort() == Type.OBJECT + && !field.desc.getClass().getName().equals(JfuncConstants.STRING_CLASS)) + || (Type.getType(field.desc).getSort() == Type.OBJECT + && field.desc.getClass().getName().equals(JfuncConstants.STRING_CLASS) + && field.access != Opcodes.ACC_FINAL)) + .map(field -> field.name).collect(Collectors.toSet()); + } + + // At present we are checking for 1) Void 2) Print-Statements 3) + // Log-Statements + private ObjectNode getReasons(MethodNode methodNode, boolean skipLogStatements, boolean skipPrintStatements) { + ObjectMapper mapper = JsonUtils.getObjectMapper(); + ObjectNode objectNode = mapper.createObjectNode(); + MethodMetaData methodMetaData = new MethodMetaData(methodNode, null); + + // First check whether method type is "Void" + if (VoidTypeUtil.isVoid(methodMetaData)) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + objectNode.set(JfuncConstants.REASONS, VoidTypeUtil.getReasons(methodMetaData)); + return objectNode; + } + + // Check for any "Print" and "Log" statements in a method + if (!(skipPrintStatements && skipLogStatements)) { + ArrayNode reasonObjectNode = mapper.createArrayNode(); + boolean isFunctionSet = false; + List internalFields = methodMetaData.getInternallyRefferedFields(); + for (InternalFeild internalField : internalFields) { + if (!skipPrintStatements && FieldUtil.containsPrintStatements(internalField)) { + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + isFunctionSet = true; + } + reasonObjectNode.add(FieldUtil.getReasonsForPrintStatements(internalField)); + } + if (!skipLogStatements && FieldUtil.containsLogStatements(internalField)) { + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + isFunctionSet = true; + } + reasonObjectNode.add(FieldUtil.getReasonsForLogStatements(internalField)); + } + } + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, true); + } else { + objectNode.set(JfuncConstants.REASONS, reasonObjectNode); + } + } + return objectNode; + } + + public List validate() { + for (ClassMetadata classMetadata : appNode.getClassMetaData()) { + List methodMetaDatas = classMetadata.getMethodMetadataList(); + for (MethodMetaData methodMetaData : methodMetaDatas) { + results.add(getReasons(methodMetaData, skipLogStatements, skipPrintStatements)); + } + } + return results; + } + + public void showResults() { + results = validate(); + for (ObjectNode node : results) { + System.out.println(node.toString()); + } + } + + @Override + public String validate(String methodName, boolean skipLogStatements, boolean skipPrintStatements) { + for (ClassMetadata classMetadata : appNode.getClassMetaData()) { + List methodMetaDatas = classMetadata.getMethodMetadataList(); + for (MethodMetaData methodMetaData : methodMetaDatas) { + if (StringUtils.equals(methodName, methodMetaData.getMethodNode().name)) { + return getReasons(new MethodMetaData(methodMetaData.getMethodNode(), classMetadata.getClassNode()), + skipLogStatements, skipPrintStatements).toString(); + } + } + } + return "No method found with name" + methodName; + } + + // At present we are checking for 1) Void 2) Print-Statements 3) + // Log-Statements + private ObjectNode getReasons(MethodMetaData methodMetaData, boolean skipLogStatements, + boolean skipPrintStatements) { + ObjectMapper mapper = JsonUtils.getObjectMapper(); + ObjectNode objectNode = mapper.createObjectNode(); + MethodNode methodNode = methodMetaData.getMethodNode(); + + // First check whether method type is "Void" + if (VoidTypeUtil.isVoid(methodMetaData)) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + objectNode.put(JfuncConstants.CLASS, methodMetaData.getClassName()); + objectNode.put(JfuncConstants.METHOD, methodNode.name); + objectNode.set(JfuncConstants.REASONS, VoidTypeUtil.getReasons(methodMetaData)); + return objectNode; + } + + // Check for any "Print" and "Log" statements in a method + if (!(skipPrintStatements && skipLogStatements)) { + ArrayNode reasonObjectNode = mapper.createArrayNode(); + boolean isFunctionSet = false; + List internalFields = methodMetaData.getInternallyRefferedFields(); + Set nonFinalFieldSet = getNonFinalFieldNodes(); + for (InternalFeild internalField : internalFields) { + if (!skipPrintStatements && FieldUtil.containsPrintStatements(internalField)) { + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + objectNode.put(JfuncConstants.CLASS, methodMetaData.getClassName()); + objectNode.put(JfuncConstants.METHOD, methodNode.name); + isFunctionSet = true; + } + reasonObjectNode.add(FieldUtil.getReasonsForPrintStatements(internalField)); + } + if (!skipLogStatements && FieldUtil.containsLogStatements(internalField)) { + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + objectNode.put(JfuncConstants.CLASS, methodMetaData.getClassName()); + objectNode.put(JfuncConstants.METHOD, methodNode.name); + isFunctionSet = true; + } + reasonObjectNode.add(FieldUtil.getReasonsForLogStatements(internalField)); + } + if (nonFinalFieldSet.contains(internalField.getName())) { + isFunctionSet = true; + objectNode.put(JfuncConstants.ISFUNCTIONAL, false); + objectNode.put(JfuncConstants.CLASS, methodMetaData.getClassName()); + objectNode.put(JfuncConstants.METHOD, methodNode.name); + reasonObjectNode.add(FieldUtil.getReasonsForGlobalFields(internalField)); + } + } + if (!isFunctionSet) { + objectNode.put(JfuncConstants.ISFUNCTIONAL, true); + objectNode.put(JfuncConstants.CLASS, methodMetaData.getClassName()); + objectNode.put(JfuncConstants.METHOD, methodNode.name); + } else { + objectNode.set(JfuncConstants.REASONS, reasonObjectNode); + } + } + return objectNode; + } + + @Override + public String validate(String methodName) { + return validate(methodName, false, false); + } } diff --git a/JFunc/src/main/java/com/jfunc/validator/JfuncConstants.java b/JFunc/src/main/java/com/jfunc/validator/JfuncConstants.java index 703efa9..638453d 100644 --- a/JFunc/src/main/java/com/jfunc/validator/JfuncConstants.java +++ b/JFunc/src/main/java/com/jfunc/validator/JfuncConstants.java @@ -8,4 +8,9 @@ public class JfuncConstants { public static final String METHODRETURNTYPE = "methodReturnType"; public static final String REASONS = "Reasons"; public static final String LINENUMBER = "Line : "; -} + public static final String GLOBAL = "Global fields are referred"; + public static final String STRING_CLASS = "java.lang.String"; + public static final String METHOD = "Method Name"; + public static final String CLASS = "Class Name"; + public static final String[] EXTENSIONS = { "class" }; +} \ No newline at end of file diff --git a/JFunc/src/main/java/com/jfunc/validator/MainApp.java b/JFunc/src/main/java/com/jfunc/validator/MainApp.java new file mode 100644 index 0000000..f64340f --- /dev/null +++ b/JFunc/src/main/java/com/jfunc/validator/MainApp.java @@ -0,0 +1,13 @@ +package com.jfunc.validator; + +import java.io.FileNotFoundException; + +public class MainApp { + + public static void main(String[] args) throws FileNotFoundException { + FunctionValidator fv = new FunctionValidator( + "/home/rajarajang/Workspace/eclipseWorkspace/JFunctionalUnit/JFunc/build/classes/"); + fv.showResults(); + } + +}