diff --git a/scope/src/main/jastadd/Navigation.jrag b/scope/src/main/jastadd/Navigation.jrag new file mode 100644 index 0000000000000000000000000000000000000000..fa9e2a4d2eedc04e2a5df36bbb3fe3480725b88f --- /dev/null +++ b/scope/src/main/jastadd/Navigation.jrag @@ -0,0 +1,13 @@ +aspect Navigation { + syn boolean Element.isScope() = false; + eq Scope.isScope() = true; + + syn Scope Element.asScope() = null; + eq Scope.asScope() = this; + + syn boolean Element.isDeclaration() = false; + eq Declaration.isDeclaration() = true; + + syn Declaration Element.asDeclaration() = null; + eq Declaration.asDeclaration() = this; +} diff --git a/scope/src/main/jastadd/ScopeTree.relast b/scope/src/main/jastadd/ScopeTree.relast new file mode 100644 index 0000000000000000000000000000000000000000..8ed72413754ad3fc998ac9d0e6e0f6a0caa4e435 --- /dev/null +++ b/scope/src/main/jastadd/ScopeTree.relast @@ -0,0 +1,4 @@ +ScopeTree : Scope; +abstract Element; +Declaration:Element ::= <Name:String>; +Scope:Element ::= Element*; diff --git a/scope/src/main/jastadd/Shadowing.jadd b/scope/src/main/jastadd/Shadowing.jadd new file mode 100644 index 0000000000000000000000000000000000000000..b15f9f6f0d912cb31869f023a2575b127fe1d04d --- /dev/null +++ b/scope/src/main/jastadd/Shadowing.jadd @@ -0,0 +1,5 @@ +aspect Shadowing { + public class VariableShadowFinding { + // add fields and getters/setters for shadowed and shadowing variable + } +} diff --git a/scope/src/main/jastadd/Shadowing.jrag b/scope/src/main/jastadd/Shadowing.jrag new file mode 100644 index 0000000000000000000000000000000000000000..a079a0adea0876780e6823aec19d9e4c4153f7d9 --- /dev/null +++ b/scope/src/main/jastadd/Shadowing.jrag @@ -0,0 +1,26 @@ +aspect Shadowing { + coll Set<VariableShadowFinding> ScopeTree.variableShadowings() [new HashSet<>()] with add root ScopeTree; + + syn int Scope.numScopes() { + int result = 1; + for (Element element : getElementList()) { + if (element.isScope()) { + result += element.asScope().numScopes(); + } + } + return result; + } + + syn int Scope.numDeclarations() { + int result = 0; + for (Element element : getElementList()) { + if (element.isScope()) { + result += element.asScope().numDeclarations(); + } else { + result++; + } + } + return result; + } + +} diff --git a/scope4j/.gitignore b/scope4j/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d8bcc397b73edab07c3ab2e5b6a547424bf1c95a --- /dev/null +++ b/scope4j/.gitignore @@ -0,0 +1,27 @@ +# Compiled class file +*.class + +# Log file +*.log + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +out/ +*.iml +/.idea/* +!.idea/codeStyles +!libs/relast.jar + +graph/ +src/gen/ +build/ diff --git a/scope4j/build.gradle b/scope4j/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..74c3ca8b598d9fb9801d589d931b351e00ef7b25 --- /dev/null +++ b/scope4j/build.gradle @@ -0,0 +1,214 @@ +buildscript { + repositories.mavenLocal() + repositories.mavenCentral() + dependencies { + classpath 'org.jastadd:jastaddgradle:1.13.3' + } +} + +apply plugin: 'java' +apply plugin: 'application' +apply plugin: 'jastadd' +apply plugin: 'idea' + +repositories { + mavenLocal() +} + +idea { + module { + generatedSourceDirs += file('./src/gen/java') + sourceDirs += file('../extendj/src/frontend') + sourceDirs += file('../extendj/src/frontend-main') + } +} + +sourceSets.main { + java { + + srcDirs "src/gen/java" + srcDirs '../extendj/src/frontend' + srcDirs '../extendj/src/frontend-main' + } + resources { + srcDir '../extendj/src/res' + srcDir jastadd.buildInfoDir + } +} + +dependencies { +} + +jastadd { + configureModuleBuild() + + modules { + + include("../extendj/jastadd_modules") + + module "scope-analysis", { + + imports "java8 frontend" + + jastadd { + basedir ".." + include "scope4j/src/gen/jastadd/Java.ast" + include "scope4j/src/gen/jastadd/Java.jadd" + include "scope4j/src/main/jastadd/*.jrag" + include "scope4j/src/main/jastadd/*.jadd" + include "scope/src/main/jastadd/*.jrag" + include "scope/src/main/jastadd/*.jadd" + + excludeFrom "java8 frontend", "grammar/ConstructorReference.ast" + excludeFrom "java8 frontend", "grammar/IntersectionCasts.ast" + excludeFrom "java8 frontend", "grammar/Lambda.ast" + excludeFrom "java8 frontend", "grammar/LambdaAnonymousDecl.ast" + excludeFrom "java8 frontend", "grammar/MethodReference.ast" + // excludeFrom "java7 frontend", "grammar/BasicTWR.ast" + excludeFrom "java7 frontend", "grammar/Diamond.ast" + excludeFrom "java7 frontend", "grammar/Literals.ast" + excludeFrom "java7 frontend", "grammar/MultiCatch.ast" + excludeFrom "java7 frontend", "grammar/TryWithResources.ast" + excludeFrom "java5 frontend", "grammar/Annotations.ast" + excludeFrom "java5 frontend", "grammar/EnhancedFor.ast" + excludeFrom "java5 frontend", "grammar/Enums.ast" + excludeFrom "java5 frontend", "grammar/GenericMethods.ast" + excludeFrom "java5 frontend", "grammar/Generics.ast" + excludeFrom "java5 frontend", "grammar/StaticImports.ast" + excludeFrom "java5 frontend", "grammar/VariableArityParameters.ast" + excludeFrom "java4 frontend", "grammar/BoundNames.ast" + excludeFrom "java4 frontend", "grammar/Java.ast" + excludeFrom "java4 frontend", "grammar/Literals.ast" + excludeFrom "java4 frontend", "grammar/NTAFinally.ast" + + excludeFrom "java7 frontend", "frontend/JavaVersion.jrag" + + excludeFrom "java5 frontend", "frontend/BytecodeReader.jrag" + excludeFrom "java7 frontend", "frontend/Variable.jadd" + } + + java { + basedir "src/main/java/" + include "**/*.java" + } + + } + + } + + + // Target module to build: + module = 'scope-analysis' + + astPackage = 'org.extendj.ast' + parser.name = 'JavaParser' + scanner.name = 'OriginalScanner' + + genDir = 'src/gen/java' + + parser.genDir = 'src/gen/java/org/extendj/parser' + scanner.genDir = 'src/gen/java/org/extendj/scanner' + + parser.genDir = 'src/gen/java/org/extendj/parser' + scanner.genDir = 'src/gen/java/org/extendj/scanner' +// +// if (project.hasProperty('extraJastAddOptions')) { +// extraJastAddOptions += project.extraJastAddOptions.split(',') as List +// print("options: ${extraJastAddOptions}") +// } + +// jastaddOptions = [ "--rewrite=cnta", "--safeLazy", "--tracing=all" ] + jastaddOptions = ["--rewrite=cnta", "--safeLazy", "--tracing=api", "--visitCheck=false"] + +// jastaddOptions = [ "--concurrent", "--rewrite=cnta", "--safeLazy" ] +// jastaddOptions = [ "--concurrent", "--rewrite=cnta", "--safeLazy", "--cache=all" ] +} + +run { + mainClassName = 'org.extendj.SccChecker' + if (project.hasProperty("appArgs")) { + args Eval.me(appArgs) + } +} + +task preprocess(type: JavaExec) { + group = 'Build' + main = "-jar" + + doFirst { + delete "src/gen/jastadd" + mkdir "src/gen/jastadd" + } + + args = [ + "../tools/relast.jar", + "src/main/jastadd/ProgramToScopeTree.relast", + "../scope/src/main/jastadd/ScopeTree.relast", + "../extendj/java8/grammar/ConstructorReference.ast", + "../extendj/java8/grammar/IntersectionCasts.ast", + "../extendj/java8/grammar/Lambda.ast", + "../extendj/java8/grammar/LambdaAnonymousDecl.ast", + "../extendj/java8/grammar/MethodReference.ast", +// "../extendj/java7/grammar/BasicTWR.ast", + "../extendj/java7/grammar/Diamond.ast", + "../extendj/java7/grammar/Literals.ast", + "../extendj/java7/grammar/MultiCatch.ast", + "../extendj/java7/grammar/TryWithResources.ast", + "../extendj/java5/grammar/Annotations.ast", + "../extendj/java5/grammar/EnhancedFor.ast", + "../extendj/java5/grammar/Enums.ast", + "../extendj/java5/grammar/GenericMethods.ast", + "../extendj/java5/grammar/Generics.ast", + "../extendj/java5/grammar/StaticImports.ast", + "../extendj/java5/grammar/VariableArityParameters.ast", + "../extendj/java4/grammar/BoundNames.ast", + "../extendj/java4/grammar/Java.ast", + "../extendj/java4/grammar/Literals.ast", + "../extendj/java4/grammar/NTAFinally.ast", + + "--listClass=ArrayList", +// "--jastAddList=JastAddList", +// "--serializer=jackson", + "--useJastAddNames", + "--file", +// "--resolverHelper", + "--grammarName=src/gen/jastadd/Java" + ] + + inputs.files file("../extendj/java8/grammar/ConstructorReference.ast"), + file("src/main/jastadd/ProgramToScopeTree.relast"), + file("../scope/src/main/jastadd/ScopeTree.relast"), + file("../extendj/java8/grammar/IntersectionCasts.ast"), + file("../extendj/java8/grammar/Lambda.ast"), + file("../extendj/java8/grammar/LambdaAnonymousDecl.ast"), + file("../extendj/java8/grammar/MethodReference.ast"), + file("../extendj/java7/grammar/BasicTWR.ast"), + file("../extendj/java7/grammar/Diamond.ast"), + file("../extendj/java7/grammar/Literals.ast"), + file("../extendj/java7/grammar/MultiCatch.ast"), + file("../extendj/java7/grammar/TryWithResources.ast"), + file("../extendj/java5/grammar/Annotations.ast"), + file("../extendj/java5/grammar/EnhancedFor.ast"), + file("../extendj/java5/grammar/Enums.ast"), + file("../extendj/java5/grammar/GenericMethods.ast"), + file("../extendj/java5/grammar/Generics.ast"), + file("../extendj/java5/grammar/StaticImports.ast"), + file("../extendj/java5/grammar/VariableArityParameters.ast"), + file("../extendj/java4/grammar/BoundNames.ast"), + file("../extendj/java4/grammar/Java.ast"), + file("../extendj/java4/grammar/Literals.ast"), + file("../extendj/java4/grammar/NTAFinally.ast"), + file("../tools/relast.jar") + outputs.files file("src/gen/jastadd/Java.ast"), + file("src/gen/jastadd/Java.jadd") +} + + +generateAst.dependsOn preprocess + +mainClassName = 'org.extendj.ScopeAnalysis' +jar.manifest.attributes 'Main-Class': mainClassName +jar.destinationDir = projectDir + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' diff --git a/scope4j/src/main/jastadd/ProgramToScopeTree.jrag b/scope4j/src/main/jastadd/ProgramToScopeTree.jrag new file mode 100644 index 0000000000000000000000000000000000000000..6a578114c145410250ab53a930521e8408fc60a4 --- /dev/null +++ b/scope4j/src/main/jastadd/ProgramToScopeTree.jrag @@ -0,0 +1,7 @@ +aspect ProgramToScopeTree { + syn lazy ScopeTree Program.scopeTree() { + ScopeTree tree = new ScopeTree(); + tree.setProgram(this); + return tree; + } +} diff --git a/scope4j/src/main/jastadd/ProgramToScopeTree.relast b/scope4j/src/main/jastadd/ProgramToScopeTree.relast new file mode 100644 index 0000000000000000000000000000000000000000..435548d30833d65bf82061133e123e8e97d18eb4 --- /dev/null +++ b/scope4j/src/main/jastadd/ProgramToScopeTree.relast @@ -0,0 +1,2 @@ +// glue relation for the Java-based variable shadowing analysis +rel ScopeTree.Program -> Program; diff --git a/scope4j/src/main/jastadd/Shadow.jrag b/scope4j/src/main/jastadd/Shadow.jrag new file mode 100644 index 0000000000000000000000000000000000000000..4052de87b00e4abdb7a4bb0d6ccc24632d30501c --- /dev/null +++ b/scope4j/src/main/jastadd/Shadow.jrag @@ -0,0 +1,3 @@ +aspect Shadow { + +} diff --git a/scope4j/src/main/java/org/extendj/JavaVariableShadowFinding.java b/scope4j/src/main/java/org/extendj/JavaVariableShadowFinding.java new file mode 100644 index 0000000000000000000000000000000000000000..3a84d7982d36fb68d732b307e81ad6bb3d3a5243 --- /dev/null +++ b/scope4j/src/main/java/org/extendj/JavaVariableShadowFinding.java @@ -0,0 +1,7 @@ +package org.extendj; + +import org.extendj.ast.VariableShadowFinding; + +public class JavaVariableShadowFinding extends VariableShadowFinding { + // add getters for the actual Java structures +} diff --git a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java new file mode 100644 index 0000000000000000000000000000000000000000..b1b8298bae91d66ce7179a413b4e334d2b2ddf6e --- /dev/null +++ b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java @@ -0,0 +1,104 @@ +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.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class ScopeAnalysis extends Frontend { + + public ScopeAnalysis() { + super("Java Scope Anaysis", ExtendJVersion.getVersion()); + } + + + /** + * Entry point for the Java checker. + * + * @param args command-line arguments + */ + public static void main(String[] args) { + + if (args.length != 1) { + System.out.println("usage: SccChecker <directory with java files>"); + System.exit(-1); + } + + try { + List<String> files = Files.walk(Paths.get(args[2])) + .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 program = new ScopeAnalysis().readProgram(files); + + // measure the time (without parsing) from here + long startGenerationTime = System.nanoTime(); + + ScopeTree scopeTree = program.scopeTree(); + long startAnalysisTime = System.nanoTime(); + + Set<VariableShadowFinding> 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); + } + } + + private Program readProgram(Collection<String> files) throws IOException { + + + 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; + } +} diff --git a/settings.gradle b/settings.gradle index 3b700667808a170cc263f25962885139a6f818eb..2dfdf27bc1b453580ff1fb73ceffd364c95c8bb5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,10 +10,12 @@ rootProject.name = 'relast-reuse' include 'statemachine' -include 'dg' -include 'simplecfg' include 'extendj' +include 'dg' include 'deps4j' +include 'scope' +include 'scope4j' +include 'simplecfg' include 'extendj:java4' include 'extendj:java5' include 'extendj:java6'