diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/GenericDeclarationList.java b/src/main/java/de/dhbwstuttgart/syntaxtree/GenericDeclarationList.java index 426b57f3..c579c464 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/GenericDeclarationList.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/GenericDeclarationList.java @@ -49,4 +49,17 @@ public class GenericDeclarationList extends SyntaxTreeNode implements Iterable(name, descriptor), signature); - return new MethodVisitor(Opcodes.ASM7) { - @Override - public void visitAttribute(Attribute attribute) { - if (attribute.type.equals("JavaTXSignature")) { - methodSignatures.put(new Pair<>(name, descriptor), ((JavaTXSignatureAttribute) attribute).signature); - } - super.visitAttribute(attribute); + var classVisitor = new ClassVisitor(Opcodes.ASM7) { + String classSignature; + @Override + public void visitAttribute(Attribute attribute) { + if (attribute.type.equals("JavaTXSignature")) { + classSignature = ((JavaTXSignatureAttribute) attribute).signature; } - }; - } - }; + super.visitAttribute(attribute); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + classSignature = signature; + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + + methodSignatures.put(new Pair<>(name, descriptor), signature); + return new MethodVisitor(Opcodes.ASM7) { + @Override + public void visitAttribute(Attribute attribute) { + if (attribute.type.equals("JavaTXSignature")) { + methodSignatures.put(new Pair<>(name, descriptor), ((JavaTXSignatureAttribute) attribute).signature); + } + super.visitAttribute(attribute); + } + }; + } + }; + + classReader.accept(classVisitor, new Attribute[]{new JavaTXSignatureAttribute()}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + classSignature = classVisitor.classSignature; + } - classReader.accept(classVisitor, new Attribute[]{new JavaTXSignatureAttribute()}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - classSignature = classVisitor.classSignature; } catch (IOException e) { // Skip } @@ -125,7 +133,7 @@ public class ASTFactory { implementedInterfaces.add((RefType) createType(jreInterface)); } - GenericDeclarationList genericDeclarationList = createGenerics(classSignature, jreClass, null); + GenericDeclarationList genericDeclarationList = createGenerics(classSignature); Token offset = new NullToken(); //Braucht keinen Offset, da diese Klasse nicht aus einem Quellcode geparst wurde @@ -154,7 +162,7 @@ public class ASTFactory { } ParameterList parameterList = new ParameterList(params, new NullToken()); Block block = new Block(new ArrayList(), new NullToken()); - GenericDeclarationList gtvDeclarations = createGenerics(signature, inClass, constructor.getName()); + GenericDeclarationList gtvDeclarations = createGenerics(signature); Token offset = new NullToken(); int modifier = constructor.getModifiers(); @@ -186,17 +194,22 @@ public class ASTFactory { } ParameterList parameterList = new ParameterList(params, new NullToken()); Block block = new Block(new ArrayList(), new NullToken()); - GenericDeclarationList gtvDeclarations = createGenerics(signature, inClass, jreMethod.getName()); + GenericDeclarationList gtvDeclarations = createGenerics(signature); Token offset = new NullToken(); return new Method(jreMethod.getModifiers(), name,returnType, parameterList, block, gtvDeclarations, offset, isInherited); } - public static GenericDeclarationList createGenerics(String signature, Class context, String methodName){ + public static GenericDeclarationList createGenerics(String signature) { + if (signature == null) return new GenericDeclarationList(new ArrayList<>(), new NullToken()); + var gtvs = new ArrayList(); var signatureVisitor = new SignatureVisitor(Opcodes.ASM7) { List bounds = new ArrayList<>(); - Stack bound = new Stack<>(); + final Stack bound = new Stack<>(); + final Stack classTypes = new Stack<>(); + + char wildcard = '='; @Override public void visitFormalTypeParameter(String name) { @@ -206,26 +219,68 @@ public class ASTFactory { @Override public void visitTypeVariable(String name) { - ((RefType) bound.peek()).getParaList().add(new GenericRefType(name, new NullToken())); + var refType = new GenericRefType(name, new NullToken()); + pushType(refType); } @Override public void visitClassType(String name) { - var currentBound = new RefType(new JavaClassName(name), new ArrayList<>(), new NullToken()); - bound.push(currentBound); + var refType = new RefType(new JavaClassName(name.replaceAll("/", ".")), new ArrayList<>(), new NullToken()); + classTypes.push(refType); + pushType(refType); + } + + void pushType(RefTypeOrTPHOrWildcardOrGeneric refType) { + if (wildcard == SignatureVisitor.SUPER) { + bound.push(new SuperWildcardType(refType, new NullToken())); + } else if (wildcard == SignatureVisitor.EXTENDS) { + bound.push(new ExtendsWildcardType(refType, new NullToken())); + } else { + bound.push(refType); + } } @Override public SignatureVisitor visitTypeArgument(char wildcard) { + this.wildcard = wildcard; return this; - } + } + + boolean equals(RefTypeOrTPHOrWildcardOrGeneric a, RefTypeOrTPHOrWildcardOrGeneric b) { + if (b instanceof SuperWildcardType wc) + return equals(a, wc.getInnerType()); + else if (b instanceof ExtendsWildcardType wc) + return equals(a, wc.getInnerType()); + return a == b; + } @Override public void visitEnd() { - var last = bound.pop(); - if (bound.empty()) bounds.add(last); - else { - ((RefType) bound.peek()).getParaList().add(last); + wildcard = '='; + var classType = (RefType) classTypes.pop(); + if (!classTypes.isEmpty()) { + var next = classTypes.peek(); + var par = bound.pop(); + var toAdd = new ArrayList(); + while (!equals(next, par)) { + toAdd.add(par); + par = bound.pop(); + } + var element = par; + if (par instanceof WildcardType wc) { + element = wc.getInnerType(); + } + Collections.reverse(toAdd); + ((RefType) element).getParaList().addAll(toAdd); + + bound.push(par); + } else { + if (bound.peek() != classType) { + classType.getParaList().add(bound.pop()); + bounds.add(classType); + } else { + bounds.add(bound.pop()); + } } } }; @@ -233,7 +288,7 @@ public class ASTFactory { var sr = new SignatureReader(signature); sr.accept(signatureVisitor); - return new GenericDeclarationList(gtvs,new NullToken()); + return new GenericDeclarationList(gtvs, new NullToken()); } private static RefTypeOrTPHOrWildcardOrGeneric createType(java.lang.reflect.Type type){ diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/type/ExtendsWildcardType.java b/src/main/java/de/dhbwstuttgart/syntaxtree/type/ExtendsWildcardType.java index 663bc9dc..2c8cffd3 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/type/ExtendsWildcardType.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/type/ExtendsWildcardType.java @@ -5,6 +5,8 @@ import de.dhbwstuttgart.syntaxtree.ASTVisitor; import de.dhbwstuttgart.typeinference.result.ResultSetVisitor; import org.antlr.v4.runtime.Token; +import java.util.Objects; + /** * Stellt eine Wildcard mit oberer Grenze dar. * z.B. void test(? extends Number var){..} @@ -54,9 +56,16 @@ public class ExtendsWildcardType extends WildcardType{ visitor.visit(this); } + @Override + public int hashCode() { + return Objects.hashCode(this.innerType); + } + @Override public boolean equals(Object o) { - // TODO Auto-generated method stub - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExtendsWildcardType that = (ExtendsWildcardType) o; + return that.innerType.equals(this.innerType); } } diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/type/GenericRefType.java b/src/main/java/de/dhbwstuttgart/syntaxtree/type/GenericRefType.java index 644dbaee..9267dbd1 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/type/GenericRefType.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/type/GenericRefType.java @@ -4,6 +4,8 @@ import de.dhbwstuttgart.syntaxtree.ASTVisitor; import de.dhbwstuttgart.typeinference.result.ResultSetVisitor; import org.antlr.v4.runtime.Token; +import java.util.Objects; + public class GenericRefType extends RefTypeOrTPHOrWildcardOrGeneric { private String name; @@ -33,14 +35,20 @@ public class GenericRefType extends RefTypeOrTPHOrWildcardOrGeneric visitor.visit(this); } - @Override - public boolean equals(Object o) { - // TODO Auto-generated method stub - return false; - } - - - @Override + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GenericRefType that = (GenericRefType) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override public String toString() { return "GTV " + this.name; diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/type/SuperWildcardType.java b/src/main/java/de/dhbwstuttgart/syntaxtree/type/SuperWildcardType.java index 6a61b9cb..8ac1820b 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/type/SuperWildcardType.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/type/SuperWildcardType.java @@ -2,9 +2,12 @@ package de.dhbwstuttgart.syntaxtree.type; import de.dhbwstuttgart.syntaxtree.ASTVisitor; +import de.dhbwstuttgart.syntaxtree.GenericTypeVar; import de.dhbwstuttgart.typeinference.result.ResultSetVisitor; import org.antlr.v4.runtime.Token; +import java.util.Objects; + /** * Stellt eine Wildcard mit unterer Grenze dar. * z.B. void test(? super Integer var){..} @@ -65,9 +68,16 @@ public class SuperWildcardType extends WildcardType{ visitor.visit(this); } + @Override + public int hashCode() { + return Objects.hashCode(this.innerType); + } + @Override public boolean equals(Object o) { - // TODO Auto-generated method stub - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SuperWildcardType that = (SuperWildcardType) o; + return that.innerType.equals(this.innerType); } } diff --git a/src/test/java/GenericsParserTest.java b/src/test/java/GenericsParserTest.java new file mode 100644 index 00000000..6e711415 --- /dev/null +++ b/src/test/java/GenericsParserTest.java @@ -0,0 +1,86 @@ +import de.dhbwstuttgart.parser.NullToken; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.syntaxtree.GenericDeclarationList; +import de.dhbwstuttgart.syntaxtree.GenericTypeVar; +import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; +import de.dhbwstuttgart.syntaxtree.type.*; +import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class GenericsParserTest { + + @Test + public void testMethodNoGenerics() { + var signature = "()V"; + var generics = ASTFactory.createGenerics(signature); + assertEquals(generics, new GenericDeclarationList(List.of(), new NullToken())); + } + + @Test + public void testMethodSimpleGenerics() { + var signature = "()V"; + var generics = ASTFactory.createGenerics(signature); + assertEquals(generics, new GenericDeclarationList( + List.of(new GenericTypeVar("T", List.of(ASTToTargetAST.OBJECT), new NullToken(), new NullToken())), new NullToken()) + ); + } + + @Test + public void testMethodExtends() { + var signature = ";>()V"; + var generics = ASTFactory.createGenerics(signature); + assertEquals(generics, new GenericDeclarationList( + List.of(new GenericTypeVar("T", List.of( + new RefType(new JavaClassName("java.lang.Class"), List.of(new GenericRefType("T", new NullToken())), new NullToken())), + new NullToken(), new NullToken())), new NullToken() + ) + ); + } + + @Test + public void testMethodVariance() { + var signature = ";>()V"; + var generics = ASTFactory.createGenerics(signature); + + assertEquals(generics, new GenericDeclarationList( + List.of(new GenericTypeVar("T", List.of( + new RefType(new JavaClassName("java.lang.Class"), List.of( + new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken()) + ), new NullToken())), + new NullToken(), new NullToken())), + new NullToken()) + ); + } + + @Test + public void testMethodComplex() { + var signature = ";>;>;U:LClassC;>()V"; + var generics = ASTFactory.createGenerics(signature); + + assertEquals(generics, new GenericDeclarationList( + List.of(new GenericTypeVar("T", List.of( + new RefType(new JavaClassName("ClassA"), List.of( + new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken()), + new RefType(new JavaClassName("ClassB"), List.of( + new ExtendsWildcardType(new RefType(new JavaClassName("ClassA"), new NullToken()), new NullToken()), + new SuperWildcardType( + new RefType(new JavaClassName("ClassC"), List.of( + new RefType(new JavaClassName("ClassA"), new NullToken()) + ), new NullToken()), new NullToken()) + ), new NullToken()) + ), new NullToken()) + ), new NullToken(), new NullToken()), + new GenericTypeVar("U", List.of( + new RefType(new JavaClassName("ClassC"), List.of( + new RefType(new JavaClassName("ClassC"), new NullToken()) + ), new NullToken()) + ), new NullToken(), new NullToken())), + new NullToken() + )); + } +}