package org.extendj; import org.extendj.ast.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.*; import java.util.stream.Collectors; public class ScopeAnalysis extends Frontend { public ScopeAnalysis() { super("Java Scope Anaysis", ExtendJVersion.getVersion()); } public Program getProgram() { return program; } private Program program; /** * Entry point for the Java checker. * * @param args command-line arguments */ public static void main(String[] args) { List<String> arguments = new ArrayList<>(Arrays.asList(args)); boolean debug = arguments.isEmpty() || arguments.remove("--debug"); boolean tree = arguments.remove("--tree"); boolean warnings = arguments.remove("--warnings"); if (arguments.size() > 1) { System.out.println("usage: ScopeAnalysis [--debug] [--tree] [--warnings] <directory with java files>"); System.exit(-1); } String path = arguments.isEmpty() ? "../testprograms/simpleScope" : arguments.get(arguments.size() - 1); if (debug) { new ScopeAnalysis().analyze(path, tree, warnings); } else { new ScopeAnalysis().analyzeTimed(path); } } public void analyzeTimed(String path) { try { List<String> files = Files.walk(Paths.get(path)) .filter(Files::isRegularFile) .filter(x -> x.getFileName().toString().endsWith(".java")).map(Path::toString).collect(Collectors.toList()); // measure the time (with parsing) from here long startMeasurementTime = System.nanoTime(); program = readProgram(files); // measure the time (without parsing) from here long startGenerationTime = System.nanoTime(); ScopeTree scopeTree = program.scopeTree(); long startAnalysisTime = System.nanoTime(); Set<AbstractFinding> findings = scopeTree.variableShadowings(); // measure the time until here long endTime = System.nanoTime(); System.out.print("java,var,false," + files.size() + "," + scopeTree.numScopes() + "," + scopeTree.numDeclarations() + "," + (scopeTree.numScopes() + scopeTree.numDeclarations()) + "," + findings.size() + ","); long parseTime = startGenerationTime - startMeasurementTime; long generationTime = startAnalysisTime - startGenerationTime; long analysisTime = endTime - startAnalysisTime; long fullTime = endTime - startMeasurementTime; System.out.print((fullTime / 1000000) + ","); System.out.print((parseTime / 1000000) + ","); System.out.print((generationTime / 1000000) + ","); System.out.print((analysisTime / 1000000) + ","); System.out.print(((generationTime + analysisTime) / 1000000) + ","); } catch (IOException e) { throw new RuntimeException(e); } } public Set<AbstractFinding> analyze(String path, boolean tree, boolean warnings) { try { List<String> files = Files.walk(Paths.get(path)) .filter(Files::isRegularFile) .filter(x -> x.getFileName().toString().endsWith(".java")).map(Path::toString).collect(Collectors.toList()); program = readProgram(files); ScopeTree scopeTree = program.scopeTree(); if (tree) { scopeTree.printAST(); } if (warnings) { System.out.println("\nExtendJ found the following problems:"); for (CompilationUnit unit : program.getCompilationUnitList()) { for (Problem problem : unit.problems()) { System.out.println(problem); } } System.out.println(); } Set<AbstractFinding> findings = scopeTree.variableShadowings(); System.out.println("\nScope4J found the following problems:"); for (AbstractFinding finding : findings) { System.out.println(finding); } System.out.println(); return findings; } catch (IOException e) { throw new RuntimeException(e); } } private Program readProgram(Collection<String> files) throws IOException { System.out.println("Reading " + (files.size() > 10 ? files.size() + " files" : files.toString())); Program program = new Program(); program.resetStatistics(); program.initBytecodeReader(Program.defaultBytecodeReader()); program.initJavaParser(Program.defaultJavaParser()); initOptions(); for (String file : files) { program.addSourceFile(file); } TypeDecl object = program.lookupType("java.lang", "Object"); if (object.isUnknown()) { // If we try to continue without java.lang.Object, we'll just get a stack overflow // in member lookups because the unknown (Object) type would be treated as circular. System.err.println("Error: java.lang.Object is missing." + " The Java standard library was not found."); throw new RuntimeException("exiting with unhandled error!"); } return program; } }