diff --git a/Switch.java b/Switch.java deleted file mode 100644 index 9f1d73c0..00000000 --- a/Switch.java +++ /dev/null @@ -1,21 +0,0 @@ - -public class Switch { - public static void main(String[] args) { - CharSequence s = "some string"; - /*var res = switch (s) { - case "AaAaAa" -> 20; - case "AaAaBB" -> 30; - case String y -> 40; - case CharSequence cs -> 30; - };*/ - - var i = s instanceof String s2; - //System.out.println(s2); - - if (!(s instanceof String s2)) { - System.out.println(s2); - } else { - System.out.println(s2); - } - } -} diff --git a/pom.xml b/pom.xml index ee4bc93f..2b995be0 100644 --- a/pom.xml +++ b/pom.xml @@ -54,8 +54,8 @@ http://maven.apache.org/maven-v4_0_0.xsd"> 3.11.0 --enable-preview - 20 - 20 + 21 + 21 diff --git a/resources/packageTest/pkg/sub/Cycle1.jav b/resources/packageTest/pkg/sub/Cycle1.jav new file mode 100644 index 00000000..79267a07 --- /dev/null +++ b/resources/packageTest/pkg/sub/Cycle1.jav @@ -0,0 +1,11 @@ +package pkg.sub; + +import java.lang.Integer; +import pkg.sub2.Cycle2; + +public class Cycle1 { + test() { + var cycle2 = new Cycle2(); + cycle2.test(); + } +} \ No newline at end of file diff --git a/resources/packageTest/pkg/sub/Test1.jav b/resources/packageTest/pkg/sub/Test1.jav new file mode 100644 index 00000000..7acebfb0 --- /dev/null +++ b/resources/packageTest/pkg/sub/Test1.jav @@ -0,0 +1,11 @@ +package pkg.sub; + +import pkg.sub2.Test2; + +public class Test1 { + + main() { + var t2 = new Test2(); + t2.test(); + } +} \ No newline at end of file diff --git a/resources/packageTest/pkg/sub2/Cycle2.jav b/resources/packageTest/pkg/sub2/Cycle2.jav new file mode 100644 index 00000000..8a13e10c --- /dev/null +++ b/resources/packageTest/pkg/sub2/Cycle2.jav @@ -0,0 +1,11 @@ +package pkg.sub2; + +import java.lang.Integer; +import pkg.sub.Cycle1; + +public class Cycle2 { + test() { + var cycle1 = new Cycle1(); + cycle1.test(); + } +} \ No newline at end of file diff --git a/resources/packageTest/pkg/sub2/Test2.jav b/resources/packageTest/pkg/sub2/Test2.jav new file mode 100644 index 00000000..a7f8927b --- /dev/null +++ b/resources/packageTest/pkg/sub2/Test2.jav @@ -0,0 +1,6 @@ +package pkg.sub2; + +public class Test2 { + + test() {} +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index 09f58d64..1c67de73 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -27,7 +27,7 @@ public class Codegen { public Codegen(TargetStructure clazz, JavaTXCompiler compiler) { this.clazz = clazz; - this.className = clazz.qualifiedName(); + this.className = clazz.qualifiedName().getClassName(); this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { @Override protected ClassLoader getClassLoader() { @@ -743,7 +743,7 @@ public class Codegen { desugared += "V"; var params = new ArrayList(); - params.add(new TargetRefType(clazz.qualifiedName())); + params.add(new TargetRefType(clazz.qualifiedName().getClassName())); params.addAll(lambda.captures().stream().map(mp -> mp.pattern().type()).toList()); var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new)); @@ -1382,7 +1382,7 @@ public class Codegen { if ((access & ACC_PRIVATE) == 0 && (access & ACC_PROTECTED) == 0) // TODO Implement access modifiers properly access |= ACC_PUBLIC; - cw.visit(V1_8, access | ACC_SUPER, clazz.qualifiedName(), generateSignature(clazz, clazz.generics()), clazz.superType() != null ? clazz.superType().getInternalName() : "java/lang/Object", clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)); + cw.visit(V1_8, access | ACC_SUPER, clazz.qualifiedName().toString().replaceAll("\\.", "/"), generateSignature(clazz, clazz.generics()), clazz.superType() != null ? clazz.superType().getInternalName() : "java/lang/Object", clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)); if (clazz.txGenerics() != null) cw.visitAttribute(new JavaTXSignatureAttribute(generateSignature(clazz, clazz.txGenerics()))); diff --git a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java index de09c24e..c38dd936 100644 --- a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java +++ b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java @@ -12,6 +12,7 @@ import de.dhbwstuttgart.parser.scope.GenericsRegistry; import de.dhbwstuttgart.parser.SyntaxTreeGenerator.SyntaxTreeGenerator; import de.dhbwstuttgart.parser.antlr.Java17Parser.SourceFileContext; import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.parser.scope.JavaClassRegistry; import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.GenericTypeVar; import de.dhbwstuttgart.syntaxtree.Method; @@ -30,6 +31,7 @@ import de.dhbwstuttgart.syntaxtree.type.TypeVisitor; import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter; import de.dhbwstuttgart.target.generate.ASTToTargetAST; import de.dhbwstuttgart.target.generate.GenericsResult; +import de.dhbwstuttgart.target.tree.expression.TargetBinaryOp; import de.dhbwstuttgart.typeinference.constraints.Constraint; import de.dhbwstuttgart.typeinference.constraints.ConstraintSet; import de.dhbwstuttgart.typeinference.constraints.Pair; @@ -72,6 +74,8 @@ public class JavaTXCompiler { public volatile UnifyTaskModel usedTasks = new UnifyTaskModel(); private final DirectoryClassLoader classLoader; + private final List classPath; + public DirectoryClassLoader getClassLoader() { return classLoader; } @@ -96,6 +100,8 @@ public class JavaTXCompiler { } classLoader = new DirectoryClassLoader(contextPath, ClassLoader.getSystemClassLoader()); environment = new CompilationEnvironment(sources); + classPath = contextPath; + for (File s : sources) { sourceFiles.put(s, parse(s)); } @@ -109,11 +115,9 @@ public class JavaTXCompiler { // Alle Importierten Klassen in allen geparsten Sourcefiles kommen ins FC for (Entry source : sourceFiles.entrySet()) { for (JavaClassName name : source.getValue().getImports()) { - // TODO: Hier werden imports von eigenen (.jav) Klassen nicht beachtet - ClassOrInterface importedClass = ASTFactory.createClass(classLoader.loadClass(name.toString())); - importedClasses.add(importedClass); + importedClasses.addAll(getAvailableClasses(name)); } - for (Class c : CompilationEnvironment.loadDefaultPackageClasses(source.getValue().getPkgName(), source.getKey(), classLoader)) { + for (Class c : CompilationEnvironment.loadDefaultPackageClasses(source.getValue().getPkgName(), source.getKey(), this)) { ClassOrInterface importedClass = ASTFactory.createClass(c); importedClasses.add(importedClass); } @@ -176,6 +180,19 @@ public class JavaTXCompiler { cl.setMethodsAdded(); } + private List getAvailableClasses(JavaClassName name) throws ClassNotFoundException { + Set allClasses = new HashSet<>(); + if (loadJavaTXClass(name)) { + var file = findFileForClass(name); + var sf = sourceFiles.get(file); + if (sf != null) allClasses.addAll(sf.KlassenVektor); + } else { + ClassOrInterface importedClass = ASTFactory.createClass(classLoader.loadClass(name.toString())); + allClasses.add(importedClass); + } + return new ArrayList<>(allClasses); + } + public List getAvailableClasses(SourceFile forSourceFile) throws ClassNotFoundException { // PL 2018-09-18: List durch Set ersetzt, damit die Klassen nur einmal // hinzugefuegt werden @@ -183,22 +200,8 @@ public class JavaTXCompiler { // ArrayList<>();//environment.getAllAvailableClasses(); Set allClasses = new HashSet<>(); - /* - * PL 2018-09-19 geloescht werden bereits in typeInference hinzugefuegt } allClasses.addAll(importedClasses); - * - * return new TYPE(sourceFiles.values(), allClasses).getConstraints(); } - * - * public List getAvailableClasses(SourceFile forSourceFile) throws ClassNotFoundException { // PL 2018-09-18: List durch Set ersetzt, damit die Klassen nur einmal // hinzugefuegt werden // List allClasses = new // ArrayList<>();//environment.getAllAvailableClasses(); Set allClasses = new HashSet<>(); - * - * /* PL 2018-09-19 geloescht werden bereits in typeInference hinzugefuegt for (SourceFile sf : sourceFiles.values()) { allClasses.addAll(sf.getClasses()); } - */ - - List importedClasses = new ArrayList<>(); for (JavaClassName name : forSourceFile.getImports()) { - // TODO: Hier werden imports von eigenen (.jav) Klassen nicht beachtet - ClassOrInterface importedClass = ASTFactory.createClass(classLoader.loadClass(name.toString())); - importedClasses.add(importedClass); - allClasses.addAll(importedClasses); + allClasses.addAll(getAvailableClasses(name)); } return new ArrayList<>(allClasses); } @@ -253,7 +256,7 @@ public class JavaTXCompiler { SourceFile sf = source.getValue(); allClasses.addAll(getAvailableClasses(sf)); allClasses.addAll(sf.getClasses()); - allClasses.addAll(CompilationEnvironment.loadDefaultPackageClasses(sf.getPkgName(), source.getKey(), classLoader).stream().map(ASTFactory::createClass).collect(Collectors.toList())); + allClasses.addAll(CompilationEnvironment.loadDefaultPackageClasses(sf.getPkgName(), source.getKey(), this).stream().map(ASTFactory::createClass).collect(Collectors.toList())); } final ConstraintSet cons = getConstraints(); @@ -262,7 +265,7 @@ public class JavaTXCompiler { // urm.addUnifyResultListener(resultListener); try { logFile = logFile == null ? new FileWriter(new File("log_" + sourceFiles.keySet().iterator().next().getName())) : logFile; - IFiniteClosure finiteClosure = UnifyTypeFactory.generateFC(allClasses, logFile, classLoader); + IFiniteClosure finiteClosure = UnifyTypeFactory.generateFC(allClasses, logFile, getClassLoader()); System.out.println(finiteClosure); urm = new UnifyResultModel(cons, finiteClosure); urm.addUnifyResultListener(resultListener); @@ -384,7 +387,7 @@ public class JavaTXCompiler { SourceFile sf = source.getValue(); allClasses.addAll(getAvailableClasses(sf)); allClasses.addAll(sf.getClasses()); - var newClasses = CompilationEnvironment.loadDefaultPackageClasses(sf.getPkgName(), source.getKey(), classLoader).stream().map(ASTFactory::createClass).collect(Collectors.toList()); + var newClasses = CompilationEnvironment.loadDefaultPackageClasses(sf.getPkgName(), source.getKey(), this).stream().map(ASTFactory::createClass).collect(Collectors.toList()); for (var clazz : newClasses) { // Don't load classes that get recompiled if (sf.getClasses().stream().anyMatch(nf -> nf.getClassName().equals(clazz.getClassName()))) @@ -622,13 +625,52 @@ public class JavaTXCompiler { return usedTPH; } + public final JavaClassRegistry classRegistry = new JavaClassRegistry(); + private SourceFile parse(File sourceFile) throws IOException, java.lang.ClassNotFoundException { SourceFileContext tree = JavaTXParser.parse(sourceFile); - SyntaxTreeGenerator generator = new SyntaxTreeGenerator(environment.getRegistry(tree, sourceFile, classLoader), new GenericsRegistry(null)); - SourceFile ret = generator.convert(tree, environment.packageCrawler, classLoader); + environment.addClassesToRegistry(classRegistry, tree, sourceFile, this); + SyntaxTreeGenerator generator = new SyntaxTreeGenerator(classRegistry, new GenericsRegistry(null)); + SourceFile ret = generator.convert(tree, environment.packageCrawler, this); return ret; } + /** + * When an import tries to import a JavaTX class it first looks it up in the cache and + * if it doesn't exist it's going to compile it and add it to the source files list + * @param name + */ + public boolean loadJavaTXClass(JavaClassName name) { + if (classRegistry.contains(name.getClassName())) { + return true; + } + + var file = findFileForClass(name); + if (file != null) { + try { + var tree = JavaTXParser.parse(file); + classRegistry.addName(name.toString(), 0); // TODO This gets overwritten later, is it bad if we don't know this right away? + environment.addClassesToRegistry(classRegistry, tree, file, this); + SyntaxTreeGenerator generator = new SyntaxTreeGenerator(classRegistry, new GenericsRegistry(null)); + sourceFiles.put(file, generator.convert(tree, environment.packageCrawler, this)); + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return false; + } + + public File findFileForClass(JavaClassName name) { + var packageName = name.getPackageName(); + var className = name.getClassName().split("\\.")[0]; + for (var cp : classPath) { + var file = new File(cp, packageName.replaceAll("\\.", "/") + "/" + className + ".jav"); + if (file.exists()) return file; + } + return null; + } + public void generateBytecode() throws ClassNotFoundException, IOException { generateBytecode((File) null); } diff --git a/src/main/java/de/dhbwstuttgart/environment/CompilationEnvironment.java b/src/main/java/de/dhbwstuttgart/environment/CompilationEnvironment.java index 79c89760..b26d1eee 100644 --- a/src/main/java/de/dhbwstuttgart/environment/CompilationEnvironment.java +++ b/src/main/java/de/dhbwstuttgart/environment/CompilationEnvironment.java @@ -7,6 +7,7 @@ import java.net.URL; import java.util.*; import com.google.common.collect.Lists; +import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; @@ -56,21 +57,22 @@ public class CompilationEnvironment { this.packageCrawler = new PackageCrawler(librarys); } - public JavaClassRegistry getRegistry(SourceFileContext tree, File sourceFile, ClassLoader classLoader) throws ClassNotFoundException, IOException { + public void addClassesToRegistry(JavaClassRegistry registry, SourceFileContext tree, File sourceFile, JavaTXCompiler compiler) throws ClassNotFoundException, IOException { Map allNames; if (tree instanceof SrcfileContext srcfile) { - allNames = GatherNames.getNames((SrcfileContext) tree, packageCrawler, classLoader); - for (Class c : loadDefaultPackageClasses(getPackageName(srcfile), sourceFile, classLoader)) { + allNames = GatherNames.getNames((SrcfileContext) tree, packageCrawler, compiler); + for (Class c : loadDefaultPackageClasses(getPackageName(srcfile), sourceFile, compiler)) { allNames.put(c.getName(), c.getTypeParameters().length); } - return new JavaClassRegistry(allNames); + registry.addNames(allNames); } else { throw new NotImplementedException(); } } - public static List loadDefaultPackageClasses(String packageName, File sourceFile, ClassLoader classLoader) throws IOException, ClassNotFoundException { + public static List loadDefaultPackageClasses(String packageName, File sourceFile, JavaTXCompiler compiler) throws IOException, ClassNotFoundException { + ClassLoader classLoader = compiler.getClassLoader(); List ret = new ArrayList<>(); // Set classLoader to include default package for this specific source file File dir = sourceFile.getParentFile(); diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/SyntaxTreeGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/SyntaxTreeGenerator.java index d0cd4f44..6fb89b23 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/SyntaxTreeGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/SyntaxTreeGenerator.java @@ -12,6 +12,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.syntaxtree.*; import de.dhbwstuttgart.syntaxtree.Record; import org.antlr.v4.runtime.CommonToken; @@ -130,7 +131,7 @@ public class SyntaxTreeGenerator { return ctx.getText(); } - public SourceFile convert(Java17Parser.SourceFileContext ctx, PackageCrawler packageCrawler, ClassLoader classLoader) throws ClassNotFoundException, NotImplementedException { + public SourceFile convert(Java17Parser.SourceFileContext ctx, PackageCrawler packageCrawler, JavaTXCompiler compiler) throws ClassNotFoundException, NotImplementedException { SrcfileContext srcfile; List classes = new ArrayList<>(); if (ctx instanceof Java17Parser.SrcfileContext) { @@ -140,7 +141,7 @@ public class SyntaxTreeGenerator { } if (srcfile.packageDeclaration() != null) this.pkgName = convert(srcfile.packageDeclaration()); - Map imports = GatherNames.getImports(srcfile, packageCrawler, classLoader); + Map imports = GatherNames.getImports(srcfile, packageCrawler, compiler); this.imports = imports.keySet().stream().map(name -> reg.getName(name)).collect(Collectors.toSet()); for (Java17Parser.ClassOrInterfaceContext type : srcfile.classOrInterface()) { ClassorinterfacedeclContext clsoif; diff --git a/src/main/java/de/dhbwstuttgart/parser/scope/GatherNames.java b/src/main/java/de/dhbwstuttgart/parser/scope/GatherNames.java index a1c9424e..d36a2d93 100644 --- a/src/main/java/de/dhbwstuttgart/parser/scope/GatherNames.java +++ b/src/main/java/de/dhbwstuttgart/parser/scope/GatherNames.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import com.google.common.collect.Iterables; +import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.environment.PackageCrawler; import de.dhbwstuttgart.exceptions.NotImplementedException; import de.dhbwstuttgart.parser.antlr.Java17Parser; @@ -25,19 +27,19 @@ import de.dhbwstuttgart.parser.antlr.Java17Parser.SubclassorinterfaceContext; public class GatherNames { - public static Map getNames(SrcfileContext ctx, PackageCrawler packages, ClassLoader classLoader) throws ClassNotFoundException { + public static Map getNames(SrcfileContext ctx, PackageCrawler packages, JavaTXCompiler compiler) throws ClassNotFoundException { Map ret = new HashMap<>(); for (Java17Parser.ClassOrInterfaceContext clsoifctx : ctx.classOrInterface()) { if (clsoifctx instanceof NoclassorinterfaceContext) { continue; } - ret.putAll(getNames(clsoifctx, getPackageName(ctx), packages, classLoader)); + ret.putAll(getNames(clsoifctx, getPackageName(ctx), packages, compiler)); } - ret.putAll(getImports(ctx, packages, classLoader)); + ret.putAll(getImports(ctx, packages, compiler)); return ret; } - public static Map getNames(ClassOrInterfaceContext clsoifctx, String pkgName, PackageCrawler packages, ClassLoader classLoader) throws ClassNotFoundException { + public static Map getNames(ClassOrInterfaceContext clsoifctx, String pkgName, PackageCrawler packages, JavaTXCompiler compiler) throws ClassNotFoundException { Map ret = new HashMap<>(); ClassorinterfacedeclContext clsoif = (ClassorinterfacedeclContext) clsoifctx; String nameString = ""; @@ -56,7 +58,7 @@ public class GatherNames { } numGenerics = clsoif.classDeclaration().genericDeclarationList() != null ? clsoif.classDeclaration().genericDeclarationList().genericTypeVar().size() : 0; ret.put(nameString, numGenerics); - ret.putAll(getNames(clsoif.classDeclaration().classBody().classBodyDeclaration(), pkgName, packages, classLoader)); + ret.putAll(getNames(clsoif.classDeclaration().classBody().classBodyDeclaration(), pkgName, packages, compiler)); break; case "EnumDeclarationContext": if (!pkgName.isEmpty()) { @@ -71,7 +73,7 @@ public class GatherNames { for (EnumConstantContext enumConstant : enumConstants.enumConstant()) { ClassBodyContext enumConstClassBody = enumConstant.classBody(); if (!Objects.isNull(enumConstClassBody)) { - ret.putAll(getNames(enumConstClassBody.classBodyDeclaration(), pkgName, packages, classLoader)); + ret.putAll(getNames(enumConstClassBody.classBodyDeclaration(), pkgName, packages, compiler)); } } } @@ -86,7 +88,7 @@ public class GatherNames { ret.put(nameString, numGenerics); for (InterfaceBodyDeclarationContext ifbody : clsoif.interfaceDeclaration().interfaceBody().interfaceBodyDeclaration()) { if (ifbody instanceof InterfacememberContext member && member.interfaceMemberDeclaration() instanceof SubclassorinterfaceContext sub) { - ret.putAll(getNames(sub.classOrInterface(), pkgName, packages, classLoader)); + ret.putAll(getNames(sub.classOrInterface(), pkgName, packages, compiler)); } } break; @@ -101,7 +103,7 @@ public class GatherNames { for (AnnotationTypeElementDeclarationContext anTypeElem : clsoif.annotationTypeDeclaration().annotationTypeBody().annotationTypeElementDeclaration()) { ClassOrInterfaceContext anClsoifctx = anTypeElem.annotationTypeElementRest().classOrInterface(); if (!Objects.isNull(anClsoifctx)) { - ret.putAll(getNames(anClsoifctx, pkgName, packages, classLoader)); + ret.putAll(getNames(anClsoifctx, pkgName, packages, compiler)); } } break; @@ -113,7 +115,7 @@ public class GatherNames { } numGenerics = clsoif.recordDeclaration().genericDeclarationList() != null ? clsoif.recordDeclaration().genericDeclarationList().genericTypeVar().size() : 0; ret.put(nameString, numGenerics); - ret.putAll(getNames(clsoif.recordDeclaration().recordBody().classBodyDeclaration(), pkgName, packages, classLoader)); + ret.putAll(getNames(clsoif.recordDeclaration().recordBody().classBodyDeclaration(), pkgName, packages, compiler)); break; default: throw new NotImplementedException(); @@ -121,24 +123,31 @@ public class GatherNames { return ret; } - public static Map getNames(List clsBodyDecl, String pkgName, PackageCrawler packages, ClassLoader classLoader) throws ClassNotFoundException { + public static Map getNames(List clsBodyDecl, String pkgName, PackageCrawler packages, JavaTXCompiler compiler) throws ClassNotFoundException { Map ret = new HashMap<>(); for (ClassBodyDeclarationContext clsbody : clsBodyDecl) { if (clsbody instanceof MemberdeclContext member && member.memberDeclaration() instanceof MemberclassorinterfaceContext memberclsoifctx) { - ret.putAll(getNames(memberclsoifctx.classOrInterface(), pkgName, packages, classLoader)); + ret.putAll(getNames(memberclsoifctx.classOrInterface(), pkgName, packages, compiler)); } } return ret; } - public static Map getImports(Java17Parser.SrcfileContext ctx, PackageCrawler packages, ClassLoader classLoader) throws ClassNotFoundException { + public static Map getImports(Java17Parser.SrcfileContext ctx, PackageCrawler packages, JavaTXCompiler compiler) throws ClassNotFoundException { Map ret = new HashMap<>(); // ret.putAll(packages.getClassNames("java.lang")); for (Java17Parser.ImportDeclarationContext importDeclCtx : ctx.importDeclaration()) { if (importDeclCtx.MUL() == null) { - Class cl = classLoader.loadClass(importDeclCtx.qualifiedName().getText()); - ret.put(cl.getName(), cl.getTypeParameters().length); + var name = importDeclCtx.qualifiedName().getText(); + var className = new JavaClassName(name); + if (compiler.loadJavaTXClass(className)) { + ret.put(name, compiler.classRegistry.getNumberOfGenerics(name)); + } else { + Class cl = compiler.getClassLoader().loadClass(name); + ret.put(cl.getName(), cl.getTypeParameters().length); + } } else if (importDeclCtx.MUL() != null) { + // TODO Find stuff in user defined packages ret.putAll(packages.getClassNames(importDeclCtx.qualifiedName().getText())); } // Die Unterscheidungen für 'static imports' wurden herausgenommen, da sie den diff --git a/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassName.java b/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassName.java index 867973f9..0426f2eb 100644 --- a/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassName.java +++ b/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassName.java @@ -12,6 +12,10 @@ import java.util.List; */ public class JavaClassName { + // FIXME It's very much possible to have imports to inner classes + // In that case a.package.Foo.Bar, a.package is the Package and Foo.Bar the class name + // Its impossible to decide what's the package based solely on the name of the class + public static final JavaClassName Void = new JavaClassName("void"); private String name; private PackageName packageName; diff --git a/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassRegistry.java b/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassRegistry.java index b2d436c7..670a21db 100644 --- a/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassRegistry.java +++ b/src/main/java/de/dhbwstuttgart/parser/scope/JavaClassRegistry.java @@ -11,11 +11,21 @@ public class JavaClassRegistry { final Map existingClasses = new HashMap<>(); public JavaClassRegistry(Map initialNames) { - for (String name : initialNames.keySet()) { - existingClasses.put(new JavaClassName(name), initialNames.get(name)); + addNames(initialNames); + } + + public JavaClassRegistry() {} + + public void addNames(Map names) { + for (String name : names.keySet()) { + existingClasses.put(new JavaClassName(name), names.get(name)); } } + public void addName(String className, int numberOfGenerics) { + existingClasses.put(new JavaClassName(className), numberOfGenerics); + } + public JavaClassName getName(String className) { for (JavaClassName name : existingClasses.keySet()) { if (name.equals(new JavaClassName(className))) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index f9e19956..2c394798 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -145,8 +145,8 @@ public class ASTToTargetAST { var methods = groupOverloads(input.getMethods()).stream().map(this::convert).flatMap(List::stream).toList(); if (input instanceof Record) - return new TargetRecord(input.getModifiers(), input.getClassName().toString(), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); - else return new TargetClass(input.getModifiers(), input.getClassName().toString(), convert(input.getSuperClass(), generics.javaGenerics), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); + return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); + else return new TargetClass(input.getModifiers(), input.getClassName(), convert(input.getSuperClass(), generics.javaGenerics), javaGenerics, txGenerics, superInterfaces, constructors, fields, methods); } private List convert(ParameterList input, GenerateGenerics generics) { diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java index 8e19a362..700815d5 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java @@ -1,22 +1,21 @@ package de.dhbwstuttgart.target.tree; -import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.target.tree.type.TargetRefType; import de.dhbwstuttgart.target.tree.type.TargetType; -import java.lang.annotation.Target; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -public record TargetClass(int modifiers, String qualifiedName, TargetType superType, Set generics, Set txGenerics, List implementingInterfaces, +public record TargetClass(int modifiers, JavaClassName qualifiedName, TargetType superType, Set generics, Set txGenerics, List implementingInterfaces, List constructors, List fields, List methods) implements TargetStructure { - public TargetClass(int modifiers, String qualifiedName) { + public TargetClass(int modifiers, JavaClassName qualifiedName) { this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new HashSet<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } - public TargetClass(int modifiers, String qualifiedName, List implementingInterfaces) { + public TargetClass(int modifiers, JavaClassName qualifiedName, List implementingInterfaces) { this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new HashSet<>(), implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java index 5e02fb58..a3dbac7b 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetRecord.java @@ -1,13 +1,13 @@ package de.dhbwstuttgart.target.tree; +import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.target.tree.type.TargetRefType; import de.dhbwstuttgart.target.tree.type.TargetType; -import java.util.Collections; import java.util.List; import java.util.Set; -public record TargetRecord(int modifiers, String qualifiedName, Set generics, Set txGenerics, List implementingInterfaces, List constructors, List fields, List methods) implements TargetStructure { +public record TargetRecord(int modifiers, JavaClassName qualifiedName, Set generics, Set txGenerics, List implementingInterfaces, List constructors, List fields, List methods) implements TargetStructure { public static final TargetType RECORD = new TargetRefType("java.lang.Record"); diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetStructure.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetStructure.java index 6227776f..4594920e 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetStructure.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetStructure.java @@ -1,5 +1,6 @@ package de.dhbwstuttgart.target.tree; +import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.target.tree.expression.TargetBlock; import de.dhbwstuttgart.target.tree.type.TargetType; @@ -8,7 +9,7 @@ import java.util.Set; public interface TargetStructure { int modifiers(); - String qualifiedName(); + JavaClassName qualifiedName(); TargetType superType(); Set generics(); Set txGenerics(); @@ -18,7 +19,7 @@ public interface TargetStructure { List methods(); default String getName() { - return qualifiedName().replaceAll("\\.", "/"); + return qualifiedName().toString().replaceAll("\\.", "/"); } // These methods are only meant to be used for test cases, a Class record should be immutable! diff --git a/src/test/java/TestPackages.java b/src/test/java/TestPackages.java new file mode 100644 index 00000000..455aac70 --- /dev/null +++ b/src/test/java/TestPackages.java @@ -0,0 +1,36 @@ +import de.dhbwstuttgart.core.JavaTXCompiler; +import org.junit.Test; + +import java.io.File; +import java.util.List; + +public class TestPackages { + + private static final String bytecodeDirectory = System.getProperty("user.dir") + "/src/test/resources/testBytecode/generatedBC/"; + + @Test + public void testPackages() throws Exception { + var cmp = new JavaTXCompiler( + List.of( + new File("resources/packageTest/pkg/sub/Test1.jav") // This should pull in Test2 + //new File("resources/packageTest/pkg/sub2/Test2.jav") + ), + List.of(new File("resources/packageTest")) + ); + + cmp.generateBytecode(bytecodeDirectory); + } + + @Test + public void testPackagesCircular() throws Exception { + var cmp = new JavaTXCompiler( + List.of( + new File("resources/packageTest/pkg/sub/Cycle1.jav") + //new File("resources/packageTest/pkg/sub2/Cycle2.jav") + ), + List.of(new File("resources/packageTest")) + ); + + cmp.generateBytecode(bytecodeDirectory); + } +} diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java index a7812172..8e0a5553 100644 --- a/src/test/java/targetast/TestCodegen.java +++ b/src/test/java/targetast/TestCodegen.java @@ -4,6 +4,7 @@ import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.bytecode.Codegen; import de.dhbwstuttgart.environment.ByteArrayClassLoader; import de.dhbwstuttgart.environment.IByteArrayClassLoader; +import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.target.generate.ASTToTargetAST; import de.dhbwstuttgart.target.tree.MethodParameter; import de.dhbwstuttgart.target.tree.TargetClass; @@ -68,14 +69,14 @@ public class TestCodegen { public static Class generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException { Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of())); var code = codegen.generate(); - writeClassFile(clazz.qualifiedName(), code); + writeClassFile(clazz.qualifiedName().getClassName(), code); return classLoader.loadClass(code); } public static Class generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader, JavaTXCompiler compiler) throws IOException { Codegen codegen = new Codegen(clazz, compiler); var code = codegen.generate(); - writeClassFile(clazz.qualifiedName(), code); + writeClassFile(clazz.qualifiedName().getClassName(), code); return classLoader.loadClass(code); } @@ -105,14 +106,14 @@ public class TestCodegen { @Test public void testEmptyClass() throws Exception { - var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Empty"); + var clazz = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("Empty")); clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), null, new TargetBlock(List.of())); generateClass(clazz, new ByteArrayClassLoader()).getDeclaredMethod("main").invoke(null); } @Test public void testArithmetic() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Arithmetic"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("Arithmetic")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), TargetType.Integer, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))))); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "sub", List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")), TargetType.Integer, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.Sub(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))))); @@ -130,7 +131,7 @@ public class TestCodegen { @Test public void testUnary() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Unary"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("Unary")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "not", List.of(new MethodParameter(TargetType.Integer, "a")), TargetType.Integer, new TargetBlock(List.of(new TargetReturn(new TargetUnaryOp.Not(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a")))))); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "neg", List.of(new MethodParameter(TargetType.Integer, "a")), TargetType.Integer, new TargetBlock(List.of(new TargetReturn(new TargetUnaryOp.Negate(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a")))))); @@ -145,7 +146,7 @@ public class TestCodegen { @Test public void testConditional() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Conditional"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("Conditional")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "and", List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")), TargetType.Boolean, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.And(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b")))))); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "or", List.of(new MethodParameter(TargetType.Boolean, "a"), new MethodParameter(TargetType.Boolean, "b")), TargetType.Boolean, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.Or(TargetType.Boolean, new TargetLocalVar(TargetType.Boolean, "a"), new TargetLocalVar(TargetType.Boolean, "b")))))); @@ -162,7 +163,7 @@ public class TestCodegen { // When adding two numbers and the return type is Long it needs to convert both values to Long @Test public void testArithmeticConvert() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "ArithmeticConvert"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("ArithmeticConvert")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", List.of(), TargetType.Long, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.Add(TargetType.Long, new TargetLiteral.CharLiteral((char) 10), new TargetLiteral.LongLiteral((long) 20)))))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); assertEquals(clazz.getDeclaredMethod("add").invoke(null), (long) 30); @@ -170,7 +171,7 @@ public class TestCodegen { @Test public void testMethodCall() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "HelloWorld"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("HelloWorld")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null, new TargetBlock(List.of(new TargetMethodCall(null, new TargetFieldVar(new TargetRefType("java.io.PrintStream"), new TargetRefType("java.lang.System"), true, new TargetClassName(new TargetRefType("java.lang.System")), "out"), List.of(new TargetLiteral.StringLiteral("Hello World!")), new TargetRefType("java.io.PrintStream"), "println", false, false)))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); @@ -179,7 +180,7 @@ public class TestCodegen { @Test public void testIfStatement() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "IfStmt"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("IfStmt")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "ifStmt", List.of(new MethodParameter(TargetType.Integer, "val")), TargetType.Integer, new TargetBlock(List.of(new TargetIf(new TargetBinaryOp.Equal(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(10)), new TargetReturn(new TargetLiteral.IntLiteral(1)), new TargetIf(new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(5)), new TargetReturn(new TargetLiteral.IntLiteral(2)), new TargetReturn(new TargetLiteral.IntLiteral(3))))))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); var ifStmt = clazz.getDeclaredMethod("ifStmt", Integer.class); @@ -190,7 +191,7 @@ public class TestCodegen { @Test public void testFor() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "For"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("For")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)), new TargetFor(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))), new TargetBlock(List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i")))))), new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum"))))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45); @@ -198,7 +199,7 @@ public class TestCodegen { @Test public void testWhile() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "While"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("While")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "whileLoop", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)), new TargetWhile(new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), new TargetBlock(List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)))))), new TargetReturn(new TargetLocalVar(TargetType.Integer, "i"))))); var clazz = generateClass(targetClass, new ByteArrayClassLoader()); assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10); @@ -206,7 +207,7 @@ public class TestCodegen { @Test public void testClassicSwitch() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "SwitchClassic"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , new JavaClassName("SwitchClassic")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchClassic", List.of(new MethodParameter(TargetType.Integer, "i")), TargetType.Integer, new TargetBlock(List.of( new TargetVarDecl(TargetType.Integer, "res", null), new TargetSwitch(new TargetLocalVar(TargetType.Integer, "i"), List.of( @@ -234,7 +235,7 @@ public class TestCodegen { @Test public void testTypeSwitch() throws Exception { - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "SwitchEnhanced"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("SwitchEnhanced")); targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchType", List.of(new MethodParameter(TargetType.Object, "obj")), TargetType.Integer, new TargetBlock(List.of( new TargetReturn(new TargetSwitch(new TargetLocalVar(TargetType.Object, "obj"), List.of( new TargetSwitch.Case(List.of(new TargetTypePattern(TargetType.String, "aString")), new TargetBlock( @@ -267,7 +268,7 @@ public class TestCodegen { // var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class")); var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer)); - var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "CGLambda"); + var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda")); targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "", false, false)))); targetClass.addMethod(Opcodes.ACC_PUBLIC, "lambda", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(interfaceType, "by2", new TargetLambdaExpression(interfaceType, List.of(), List.of(new MethodParameter(TargetType.Integer, "num")), TargetType.Integer, new TargetBlock(List.of(new TargetReturn(new TargetBinaryOp.Mul(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "num"), new TargetLiteral.IntLiteral(2))))))), new TargetReturn(new TargetCast(TargetType.Integer, new TargetMethodCall(TargetType.Object, TargetType.Object, List.of(TargetType.Object), new TargetLocalVar(interfaceType, "by2"), List.of(new TargetLiteral.IntLiteral(10)), interfaceType, "apply", false, true)))))); var clazz = generateClass(targetClass, classLoader);