Merge branch 'patternMatching' of ssh://gitea.hb.dhbw-stuttgart.de:2222/JavaTX/JavaCompilerCore into patternMatching
Some checks failed
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 5m39s

This commit is contained in:
pl@gohorb.ba-horb.de 2024-11-24 08:15:12 +01:00
commit edf609f916
45 changed files with 1400 additions and 707 deletions

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
java-version: '22'
cache: 'maven'
- name: Compile project
run: |

View File

@ -1,6 +0,0 @@
sealed interface List<T> permits LinkedElem, Elem {}
record LinkedElem<T>(T a, List<T> l) implements List<T> {}
record Elem<T>(T a) implements List<T> {}
public class SwitchAppend {}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -2147,6 +2147,18 @@ public class Java17ParserBaseListener implements Java17ParserListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitEnclosedPattern(Java17Parser.EnclosedPatternContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLPattern(Java17Parser.LPatternContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLPattern(Java17Parser.LPatternContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -1257,6 +1257,13 @@ public class Java17ParserBaseVisitor<T> extends AbstractParseTreeVisitor<T> impl
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitEnclosedPattern(Java17Parser.EnclosedPatternContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLPattern(Java17Parser.LPatternContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -1960,6 +1960,18 @@ public interface Java17ParserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitEnclosedPattern(Java17Parser.EnclosedPatternContext ctx);
/**
* Enter a parse tree produced by the {@code lPattern}
* labeled alternative in {@link Java17Parser#primaryPattern}.
* @param ctx the parse tree
*/
void enterLPattern(Java17Parser.LPatternContext ctx);
/**
* Exit a parse tree produced by the {@code lPattern}
* labeled alternative in {@link Java17Parser#primaryPattern}.
* @param ctx the parse tree
*/
void exitLPattern(Java17Parser.LPatternContext ctx);
/**
* Enter a parse tree produced by {@link Java17Parser#recordPattern}.
* @param ctx the parse tree

View File

@ -1164,6 +1164,13 @@ public interface Java17ParserVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitEnclosedPattern(Java17Parser.EnclosedPatternContext ctx);
/**
* Visit a parse tree produced by the {@code lPattern}
* labeled alternative in {@link Java17Parser#primaryPattern}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLPattern(Java17Parser.LPatternContext ctx);
/**
* Visit a parse tree produced by {@link Java17Parser#recordPattern}.
* @param ctx the parse tree

View File

@ -54,8 +54,8 @@ http://maven.apache.org/maven-v4_0_0.xsd">
<version>3.11.0</version>
<configuration>
<compilerArgs>--enable-preview</compilerArgs>
<source>21</source>
<target>21</target>
<source>22</source>
<target>22</target>
</configuration>
</plugin>
<plugin>

View File

@ -0,0 +1,15 @@
import java.lang.Integer;
public class Assign {
public x = 10;
public y = this;
public call() {
return this;
}
public m() {
this.call().call().y.x = 20;
return x;
}
}

View File

@ -12,7 +12,6 @@ public class GenericRecordSwitchCase {
return switch(o) {
case LinkedElem(a, Elem(e)) -> a ;
case LinkedElem(a, LinkedElem(e, Elem(f))) -> a;
default -> null;
};
}
}

View File

@ -0,0 +1,20 @@
import java.lang.Object;
import java.lang.Integer;
import java.lang.Float;
public record R(Object nested) {}
public class OverloadNestedPattern {
public Integer m(R(R(Integer a)), R(Integer b)) {
return 1;
}
public Integer m(R(R(Float a)), R(Float b)) {
return 2;
}
public Integer m(R(Integer a), R(Integer b)) {
return 3;
}
}

View File

@ -2,15 +2,31 @@ import java.lang.Integer;
import java.lang.Number;
import java.lang.Float;
record Point(Number x, Number y) {}
public record Point(Number x, Number y) {}
public class OverloadPattern {
public m(Point(Integer x, Integer y)) {
return x + y;
public Number m(Point(Integer x, Integer y), Point(Float a, Float b)) {
return 1;
}
public m(Point(Float x, Float y)) {
return x * y;
public Number m(Point(Integer x, Integer y), Point(Integer a, Integer b)) {
return 2;
}
public Number m(Point(Float x, Float y), Point(Integer a, Integer b)) {
return 3;
}
public Number m(Point(Float x, Float y), Point(Float a, Float b)) {
return 4;
}
public Number m(Point(Integer x, Integer y)) {
return 5;
}
public Number m(Point(Float x, Float y)) {
return 6;
}
public m(Integer x) {

View File

@ -0,0 +1,21 @@
import java.lang.Boolean;
sealed interface List<T> permits LinkedElem, Elem {}
public record LinkedElem<T>(T a, List<T> l) implements List<T> {}
public record Elem<T>(T a) implements List<T> {}
public class PatternMatchingHaskellStyle {
public append(LinkedElem(a, b), list2) {
return handleAppend(a, b, list2);
}
private handleAppend(a, Elem(e), list2) {
return new LinkedElem<>(a, new LinkedElem<>(e, list2));
}
private handleAppend(a, LinkedElem(e,r), list2) {
return new LinkedElem<>(a, append(new LinkedElem(e, r), list2));
}
}

View File

@ -0,0 +1,19 @@
import java.lang.Boolean;
import java.lang.Object;
sealed interface List<T> permits Cons, Empty {}
public record Cons<T>(T a, List<T> l) implements List<T> {}
public record Empty<T>() implements List<T> {}
public class PatternMatchingListAppend {
public append(Cons(a, b), list2) {
return new Cons<>(a, append(b, list2));
}
public append(Empty(), list2) {
return list2;
}
}

View File

@ -0,0 +1,17 @@
import java.lang.Integer;
import java.lang.Double;
import java.lang.Number;
import java.lang.String;
public record R(Number n) {}
public class SwitchOverload {
public f(){}
public m(r) {
return switch(r) {
case R("test") -> f();
};
}
}

View File

@ -10,7 +10,7 @@ public class SwitchAppend {
case LinkedElem(a, r) -> new LinkedElem<>(a, append(r, l2));
//Alternativ:
//case LinkedElem(a, LinkedElem(e, r)) -> new LinkedElem<>(a, append(new LinkedElem(e, r), l2));
default -> null;
//default -> null;
};
}
}

View File

@ -0,0 +1,14 @@
import java.lang.Object;
import java.lang.Integer;
public record R(Integer i) {}
public class SwitchNestedValue {
public main(r) {
return switch(r) {
case R(10) -> 1;
case R(20) -> 2;
case R(i) -> 3;
};
}
}

View File

@ -0,0 +1,21 @@
import java.lang.Integer;
import java.lang.Double;
import java.lang.Number;
public record R(Number n) {}
public class SwitchOverload {
Number f(Double d) { return d * 2; }
Number f(Integer i) { return i * 5; }
public m(r, x) {
x = x + x;
return switch(r) {
case R(o) -> {
x = x + x;
yield f(o);
}
};
}
}

View File

@ -654,6 +654,7 @@ primaryPattern
: typePattern #tPattern
| recordPattern #rPattern
| '(' pattern ')' #enclosedPattern
| literal (',' literal)* #lPattern
;
recordPattern

View File

@ -13,6 +13,7 @@ import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.*;
import org.antlr.v4.codegen.Target;
import org.objectweb.asm.*;
import java.lang.invoke.*;
@ -108,6 +109,10 @@ public class Codegen {
this.scope = this.scope.parent;
}
LocalVar createVariable(TargetType type) {
return createVariable("__var" + this.localCounter, type);
}
LocalVar createVariable(String name, TargetType type) {
var local = new LocalVar(localCounter, name, type);
scope.add(local);
@ -1294,8 +1299,7 @@ public class Codegen {
state.enterScope();
// This is the index to start the switch from
mv.visitInsn(ICONST_0);
if (aSwitch.isExpression())
state.pushSwitch();
state.pushSwitch();
// To be able to skip ahead to the next case
var start = new Label();
@ -1393,8 +1397,10 @@ public class Codegen {
state.breakStack.pop();
if (aSwitch.isExpression()) {
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
unboxPrimitive(state, aSwitch.type());
if (aSwitch.type() != null) {
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
unboxPrimitive(state, aSwitch.type());
}
state.popSwitch();
}
@ -1431,7 +1437,24 @@ public class Codegen {
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
}
if (pat instanceof TargetTypePattern sp) {
if (pat instanceof TargetExpressionPattern ep) {
var cur = state.createVariable(pat.type());
state.mv.visitVarInsn(ASTORE, cur.index);
var expr = new Equal(pat.type(), new TargetLocalVar(cur.type, cur.name), ep.expression());
generate(state, expr);
var cont = new Label();
state.mv.visitJumpInsn(IFNE, cont);
for (var i = 0; i < depth - 1; i++) {
state.mv.visitInsn(POP);
}
state.mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
state.mv.visitLdcInsn(index + 1);
state.mv.visitJumpInsn(GOTO, start);
state.mv.visitLabel(cont);
} else if (pat instanceof TargetTypePattern sp) {
var local = state.createVariable(sp.name(), sp.type());
state.mv.visitVarInsn(ASTORE, local.index);
} else if (pat instanceof TargetComplexPattern cp) {
@ -1522,29 +1545,26 @@ public class Codegen {
mv.visitEnd();
}
private int bindLocalVariables(State state, TargetPattern pattern, int offset, int field) {
if (pattern instanceof TargetComplexPattern cp) {
state.mv.visitVarInsn(ALOAD, offset);
private void bindLocalVariables(State state, TargetComplexPattern cp, int offset) {
state.mv.visitVarInsn(ALOAD, offset);
var clazz = findClass(new JavaClassName(cp.type().name()));
if (clazz == null) throw new CodeGenException("Class definition for '" + cp.type().name() + "' not found");
var clazz = findClass(new JavaClassName(cp.type().name()));
if (clazz == null) throw new CodeGenException("Class definition for '" + cp.type().name() + "' not found");
for (var i = 0; i < cp.subPatterns().size(); i++) {
var subPattern = cp.subPatterns().get(i);
for (var i = 0; i < cp.subPatterns().size(); i++) {
var subPattern = cp.subPatterns().get(i);
if (i < cp.subPatterns().size() - 1)
state.mv.visitInsn(DUP);
if (i < cp.subPatterns().size() - 1)
state.mv.visitInsn(DUP);
extractField(state, cp.type(), i, clazz);
state.mv.visitTypeInsn(CHECKCAST, subPattern.type().getInternalName());
state.mv.visitVarInsn(ASTORE, offset);
offset = bindLocalVariables(state, subPattern, offset, i);
extractField(state, cp.type(), i, clazz);
state.mv.visitTypeInsn(CHECKCAST, subPattern.type().getInternalName());
offset = state.createVariable(subPattern.name(), subPattern.type()).index;
state.mv.visitVarInsn(ASTORE, offset);
if (subPattern instanceof TargetComplexPattern cp2) {
bindLocalVariables(state, cp2, offset);
}
} else if (pattern instanceof TargetTypePattern tp) {
offset++;
state.createVariable(tp.name(), tp.type());
} else throw new NotImplementedException();
return offset;
}
}
private void generateMethod(TargetMethod method) {
@ -1563,8 +1583,14 @@ public class Codegen {
if (method.block() != null) {
mv.visitCode();
var state = new State(method.signature().returnType(), mv, method.isStatic() ? 0 : 1);
var offset = 1;
for (var param : method.signature().parameters()) {
bindLocalVariables(state, param.pattern(), 1, 0);
state.createVariable(param.pattern().name(), param.pattern().type());
}
for (var param : method.signature().parameters()) {
if (param.pattern() instanceof TargetComplexPattern cp)
bindLocalVariables(state, cp, offset);
offset++;
}
generate(state, method.block());
if (method.signature().returnType() == null)

View File

@ -14,6 +14,8 @@ import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.Void;
import de.dhbwstuttgart.target.tree.expression.TargetUnaryOp;
import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
import org.antlr.v4.runtime.Token;
import de.dhbwstuttgart.exceptions.NotImplementedException;
@ -101,7 +103,6 @@ import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import org.stringtemplate.v4.ST;
public class StatementGenerator {
@ -152,7 +153,7 @@ public class StatementGenerator {
: TypePlaceholder.fresh(fp.getStart());
}
ret.add(new FormalParameter(paramName, type, fp.getStart()));
localVars.put(paramName, type);
localVars.put(paramName, type);
}
}
return new ParameterList(ret, ret.get(0).getOffset());
@ -441,6 +442,7 @@ public class StatementGenerator {
}
private Pattern convert(PatternContext pattern) {
return switch (pattern) {
case PPatternContext pPattern -> {
yield convert(pPattern.primaryPattern());
@ -455,6 +457,7 @@ public class StatementGenerator {
}
private FormalParameter convert(PrimaryPatternContext pPattern) {
switch (pPattern) {
case TPatternContext tPattern:
TypePatternContext typePattern = tPattern.typePattern();
@ -465,21 +468,25 @@ public class StatementGenerator {
case RPatternContext rPattern:
RecordPatternContext recordPattern = rPattern.recordPattern();
return convert(recordPattern);
default:
throw new NotImplementedException();
case Java17Parser.LPatternContext patternContext: return new LiteralPattern(TypePlaceholder.fresh(patternContext.start), convert(patternContext.literal().get(0)), patternContext.start);
default:
throw new NotImplementedException();
}
}
private RecordPattern convert(RecordPatternContext recordPatternCtx) {
List<PatternContext> subPatternCtx = recordPatternCtx.recordStructurePattern().recordComponentPatternList().pattern();
var cpl = recordPatternCtx.recordStructurePattern().recordComponentPatternList();
List<PatternContext> subPatternCtx = cpl == null ? List.of() : cpl.pattern();
List<Pattern> subPattern = subPatternCtx.stream().map(this::convert).collect(Collectors.toList());
IdentifierContext identifierCtx = recordPatternCtx.identifier();
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
var type = recordPatternCtx.typeType() == null ? TypePlaceholder.fresh(recordPatternCtx.getStart()) : TypeGenerator.convert(recordPatternCtx.typeType(), reg, generics);
if (text != null) localVars.put(text, type);
return new RecordPattern(subPattern, text, type, recordPatternCtx.getStart());
var ret = new RecordPattern(subPattern, text, type, recordPatternCtx.getStart());
return ret;
}
private Statement convert(Java17Parser.WhileloopContext stmt) {
@ -586,7 +593,7 @@ public class StatementGenerator {
initValue = convert(varDecl.variableInitializer().expression());
}
var fieldEntry = fields.get(name.getText());
return (new Assign(new AssignToField(new FieldVar(new This(varDecl.getStart()), (fieldEntry.modifiers() & Modifier.STATIC) != 0, name.getText(), type, varDecl.getStart())), initValue, name.getStart()));
return (new Assign(new AssignToField(new FieldVar(new This(varDecl.getStart()), name.getText(), type, varDecl.getStart())), initValue, name.getStart()));
}
private Statement convert(Java17Parser.BreakstmtContext stmt) {
@ -700,8 +707,7 @@ public class StatementGenerator {
if (!Objects.isNull(expr.methodCall())) {
return convert(expr.methodCall(), expr.expression(), offset);
} else if (!Objects.isNull(expr.identifier())) {
// FIXME This is not the right way of handling any of this
return generateLocalOrFieldVarOrClassName(expr.getText(), offset);
return new FieldVar(convert(expr.expression()), expr.identifier().getText(), TypePlaceholder.fresh(expr.identifier().start), offset);
} else {
// Für alle anderen Optionen, wie Feldzugriff, Aufrufe von super oder explizite
// generische Invokationen
@ -777,64 +783,25 @@ public class StatementGenerator {
/**
* Der Parser kann nicht zwischen einer lokalen Variable, einem Feldzugriff und einer Klassenangabe unterscheiden.
*
*
* @param expression
* @param offset
* @return
*/
private Expression generateLocalOrFieldVarOrClassName(String expression, Token offset) {
// FIXME Why does this take a String argument???
String[] parts = expression.split("\\.");
if (parts.length < 2) {
// Check for localVar:
if (localVars.get(expression) != null) {
return new LocalVar(expression, localVars.get(expression), offset);
} else if (fields.get(expression) != null) {// PL 2018-11-01 fields eingefuegt, damit die fields immer die
// gleiche TPH bekommen
var field = fields.get(expression);
return new FieldVar(new This(offset), Modifier.isStatic(field.modifiers()), expression, fields.get(expression).type(), offset);
} else if (reg.contains(expression)) {
return generateStaticClassName(expression, offset);
} else {
// lokale Variable wurde ohne "var"-Keyword deklariert und direkt mit Wert versehen
localVars.put(expression, TypePlaceholder.fresh(offset));
return new LocalVar(expression, localVars.get(expression), offset);
}
}
return generateFieldVarOrClassname(expression, offset);
}
private Expression generateFieldVarOrClassname(String expression, Token offset) {
String[] parts = expression.split("\\.");
String whole = "";
Expression receiver = null;
for (String part : parts) {
whole += part;
// Check for Classname:
if (reg.contains(whole)) {
receiver = generateStaticClassName(whole, offset);
break;
}
whole += ".";
}
var fieldName = parts[parts.length - 1];
var isStatic = false;
if (parts.length < 2 || parts[0].contentEquals("this")) {
receiver = new This(offset);
isStatic = Modifier.isStatic(fields.get(fieldName).modifiers());
} else if (parts[0].contentEquals("super")) {
receiver = new Super(TypePlaceholder.fresh(offset), offset);
isStatic = Modifier.isStatic(compiler.getClass(new JavaClassName(superClass.getName().toString())).getField(fieldName).orElseThrow().modifier);
} else if (receiver == null) { // Handelt es sich um keinen Statischen Klassennamen:
String part = expression.substring(0, expression.length() - (1 + parts[parts.length - 1].length()));
receiver = generateLocalOrFieldVarOrClassName(part, offset);
// Check for localVar:
if (localVars.get(expression) != null) {
return new LocalVar(expression, localVars.get(expression), offset);
} else if (fields.get(expression) != null) {// PL 2018-11-01 fields eingefuegt, damit die fields immer die
// gleiche TPH bekommen
return new FieldVar(new This(offset), expression, fields.get(expression).type(), offset);
} else if (reg.contains(expression)) {
return generateStaticClassName(expression, offset);
} else {
StaticClassName cname = (StaticClassName) receiver;
var javaClassName = reg.getName(cname.getType().toString());
isStatic = Modifier.isStatic(compiler.getClass(javaClassName).getField(fieldName).orElseThrow().modifier);
// lokale Variable wurde ohne "var"-Keyword deklariert und direkt mit Wert versehen
localVars.put(expression, TypePlaceholder.fresh(offset));
return new LocalVar(expression, localVars.get(expression), offset);
}
return new FieldVar(receiver, isStatic, fieldName, TypePlaceholder.fresh(offset), offset);
}
private Expression convert(Java17Parser.ArrayaccessexpressionContext arrayaccess) {
@ -870,10 +837,8 @@ public class StatementGenerator {
private Statement convert(AssignexpressionContext expr) {
switch (expr.bop.getText()) {
case "=":
ExpressionContext leftside = expr.expression(0);
AssignLeftSide leftHandSide = convert(leftside.getText(), leftside.getStart());
Statement ret = new Assign(leftHandSide, convert(expr.expression(1)), expr.getStart());
return ret;
AssignLeftSide leftHandSide = convertAssignLHS(convert(expr.expression(0)));
return new Assign(leftHandSide, convert(expr.expression(1)), expr.getStart());
case "+=":
case "-=":
case "*=":
@ -890,8 +855,8 @@ public class StatementGenerator {
}
}
private AssignLeftSide convert(String leftHandSide, Token start) {
Expression leftSide = generateLocalOrFieldVarOrClassName(leftHandSide, start);
private AssignLeftSide convertAssignLHS(Expression expr) {
Expression leftSide = expr;
if (leftSide instanceof FieldVar)
return new AssignToField((FieldVar) leftSide);
else if (leftSide instanceof LocalVar)

View File

@ -269,7 +269,7 @@ public class SyntaxTreeGenerator {
}
fielddecl.add(new Field(fieldname, fieldtype, fieldmodifiers, fieldoffset));
constructorParameters.add(new FormalParameter(fieldname, fieldtype, fieldoffset));
FieldVar fieldvar = new FieldVar(new This(offset), false, fieldname, fieldtype, fieldoffset);
FieldVar fieldvar = new FieldVar(new This(offset), fieldname, fieldtype, fieldoffset);
constructorStatements.add(new Assign(new AssignToField(fieldvar), new LocalVar(fieldname, fieldtype, fieldoffset), offset));
Statement returnStatement = new Return(fieldvar, offset);
methods.add(new Method(allmodifiers.get("public"), fieldname, fieldtype, new ParameterList(new ArrayList<>(), offset), new Block(Arrays.asList(returnStatement), offset), new GenericDeclarationList(new ArrayList<>(), offset), offset));

View File

@ -10,6 +10,8 @@ public interface ASTVisitor extends StatementVisitor{
void visit(FormalParameter formalParameter);
void visit(LiteralPattern literalPattern);
void visit(GenericDeclarationList genericTypeVars);
void visit(Field field);

View File

@ -37,6 +37,11 @@ public abstract class AbstractASTWalker implements ASTVisitor {
formalParameter.getType().accept((ASTVisitor) this);
}
@Override
public void visit(LiteralPattern literalPattern) {
}
@Override
public void visit(GenericDeclarationList genericTypeVars) {
Iterator<GenericTypeVar> genericIterator = genericTypeVars.iterator();

View File

@ -0,0 +1,29 @@
package de.dhbwstuttgart.syntaxtree;
import de.dhbwstuttgart.syntaxtree.ASTVisitor;
import de.dhbwstuttgart.syntaxtree.Pattern;
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.syntaxtree.statement.Expression;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceBlockInformation;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import org.antlr.v4.runtime.Token;
public class LiteralPattern extends FormalParameter
{
public final Expression value;
public LiteralPattern(RefTypeOrTPHOrWildcardOrGeneric type, Expression value, Token offset) {
super(null, type, offset);
this.value = value;
}
@Override
public FormalParameter withType(RefTypeOrTPHOrWildcardOrGeneric type) {
return new LiteralPattern(type, value, getOffset());
}
@Override
public void accept(ASTVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -3,6 +3,7 @@ package de.dhbwstuttgart.syntaxtree;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.statement.*;
public interface StatementVisitor {
void visit(ArgumentList argumentList);

View File

@ -34,7 +34,6 @@ public class ASTFactory {
private static final HashMap<java.lang.Class, ClassOrInterface> cache = new HashMap<>();
public static ClassOrInterface createClass(java.lang.Class jreClass) {
System.out.println(jreClass);
if (cache.containsKey(jreClass))
return cache.get(jreClass);
@ -174,7 +173,6 @@ public class ASTFactory {
superClass = (RefType) createType(java.lang.Object.class);
}
List<RefType> implementedInterfaces = new ArrayList<>();
System.out.println(jreClass);
for (Type jreInterface : jreClass.getGenericInterfaces()) {
implementedInterfaces.add((RefType) createType(jreInterface));
}

View File

@ -17,13 +17,11 @@ public class FieldVar extends Expression {
public final String fieldVarName;
public final Expression receiver;
public final boolean isStatic;
public FieldVar(Expression receiver, boolean isStatic, String fieldVarName, RefTypeOrTPHOrWildcardOrGeneric type, Token offset) {
public FieldVar(Expression receiver, String fieldVarName, RefTypeOrTPHOrWildcardOrGeneric type, Token offset) {
super(type, offset);
this.fieldVarName = fieldVarName;
this.receiver = receiver;
this.isStatic = isStatic;
}
@Override

View File

@ -8,6 +8,7 @@ import org.antlr.v4.runtime.Token;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
public class RefType extends RefTypeOrTPHOrWildcardOrGeneric
@ -49,10 +50,7 @@ public class RefType extends RefTypeOrTPHOrWildcardOrGeneric
@Override
public int hashCode() {
int hash = 0;
hash += super.hashCode();
hash += this.name.hashCode();//Nur den Name hashen. Sorgt ¼r langsame, aber funktionierende HashMaps
return hash;
return this.name.hashCode();//Nur den Name hashen. Sorgt ¼r langsame, aber funktionierende HashMaps
}
public RefType(JavaClassName fullyQualifiedName, List<RefTypeOrTPHOrWildcardOrGeneric> parameter, Token offset) {
@ -83,6 +81,7 @@ public class RefType extends RefTypeOrTPHOrWildcardOrGeneric
public boolean equals(Object obj)
{
if(obj instanceof RefType){
if (!Objects.equals(this.name, ((RefType) obj).name)) return false;
boolean ret = true;
//if(!(super.equals(obj))) PL 2020-03-12 muss vll. einkommentiert werden

View File

@ -57,6 +57,11 @@ public class OutputGenerator implements ASTVisitor {
out.append(formalParameter.getName());
}
@Override
public void visit(LiteralPattern literalPattern) {
literalPattern.value.accept(this);
}
@Override
public void visit(GenericDeclarationList genericTypeVars) {
Iterator<GenericTypeVar> genericIterator = genericTypeVars.iterator();
@ -508,12 +513,11 @@ public class OutputGenerator implements ASTVisitor {
aRecordPattern.getType().accept(this);
out.append("(");
List<Pattern> subPatterns = aRecordPattern.getSubPattern();
int i;
for (i = 0; i < subPatterns.size() - 1; i++) {
for (var i = 0; i < subPatterns.size(); i++) {
subPatterns.get(i).accept(this);
out.append(", ");
if (i < subPatterns.size() - 1)
out.append(", ");
}
subPatterns.get(i).accept(this);
String name;
if ((name = aRecordPattern.getName()) != null)
out.append(name);

View File

@ -5,6 +5,7 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*;
@ -17,6 +18,7 @@ import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import java.sql.Array;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -33,12 +35,18 @@ public class ASTToTargetAST {
protected List<Generics> all;
public Generics generics;
public List<Generics> currentMethodOverloads;
final Map<ClassOrInterface, Set<GenericTypeVar>> userDefinedGenerics = new HashMap<>();
final Map<Method, Set<SignaturePair>> tphsInMethods = new HashMap<>();
private Method currentMethod;
public final JavaTXCompiler compiler;
public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) {
return javaGenerics().stream().map(generics -> generics.resolve(type)).distinct().toList();
}
public List<GenericsResult> txGenerics() {
return all.stream().map(generics -> new GenericsResult(generics.txGenerics)).toList();
}
@ -138,37 +146,60 @@ public class ASTToTargetAST {
return ret;
}
// This is used to serve as a custom equality to signature that performs a weak check without going into record patterns.
// The two signatures are considered equal if all the argument types match.
record WeakSignature(TargetMethod.Signature signature) {
@Override
public boolean equals(Object o) {
if (!(o instanceof WeakSignature other)) return false;
if (other.signature.parameters().size() != signature.parameters().size()) return false;
for (var i = 0; i < signature.parameters().size(); i++) {
var p1 = signature.parameters().get(i).pattern().type();
var p2 = other.signature.parameters().get(i).pattern().type();
if (!p1.equals(p2)) return false;
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(signature.parameters().stream().map(p -> p.pattern().type()).toArray());
}
}
public List<List<TargetMethod>> groupOverloads(ClassOrInterface input, List<Method> methods) {
var res = new ArrayList<List<TargetMethod>>();
var mapOfSignatures = new HashMap<WeakSignature, List<MethodWithTphs>>();
for (var method : methods) {
// Convert all methods
var methodsWithTphs = convert(input, method);
// Then check for methods with the same signature
var mapOfSignatures = new HashMap<TargetMethod.Signature, List<MethodWithTphs>>();
for (var m : methodsWithTphs) {
var methodsWithSameSignature = mapOfSignatures.getOrDefault(m.method.signature(), new ArrayList<>());
var signature = new WeakSignature(m.method.signature());
var methodsWithSameSignature = mapOfSignatures.getOrDefault(signature, new ArrayList<>());
methodsWithSameSignature.add(m);
mapOfSignatures.put(m.method.signature(), methodsWithSameSignature);
mapOfSignatures.put(signature, methodsWithSameSignature);
}
}
for (var methodsWithSignature : mapOfSignatures.values()) {
var resMethods = new HashSet<TargetMethod>();
for (var methodsWithSignature : mapOfSignatures.values()) {
outer: for (var m1 : methodsWithSignature) {
for (var m2 : methodsWithSignature) {
for (var i = 0; i < m1.args.size(); i++) {
var arg1 = m1.args.get(i);
var arg2 = m2.args.get(i);
if (arg1.parameter.equals(arg2.parameter)) {
if (isSupertype(arg1.signature, arg2.signature) &&
outer: for (var m1 : methodsWithSignature) {
for (var m2 : methodsWithSignature) {
for (var i = 0; i < m1.args.size(); i++) {
var arg1 = m1.args.get(i);
var arg2 = m2.args.get(i);
if (arg1.parameter.equals(arg2.parameter)) {
if (isSupertype(arg1.signature, arg2.signature) &&
!arg1.signature.equals(arg2.signature)) continue outer;
}
}
}
resMethods.add(m1.method);
}
resMethods.add(m1.method);
}
res.add(resMethods.stream().toList());
}
return res;
}
@ -202,7 +233,7 @@ public class ASTToTargetAST {
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics)).toList();
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer)).flatMap(List::stream).toList();
var fields = input.getFieldDecl().stream().map(this::convert).toList();
var methods = groupOverloads(input, input.getMethods()).stream().flatMap(List::stream).toList();
var methods = groupOverloads(input, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream).toList();
TargetMethod staticConstructor = null;
if (input.getStaticInitializer().isPresent())
@ -216,9 +247,14 @@ public class ASTToTargetAST {
}
public List<MethodParameter> convert(ParameterList input, GenerateGenerics generics) {
return input.getFormalparalist().stream().map(param ->
new MethodParameter((TargetPattern) convert(param))
).toList();
var res = new ArrayList<MethodParameter>();
for (var i = 0; i < input.getFormalparalist().size(); i++) {
var param = input.getFormalparalist().get(i);
var pattern = (TargetPattern) convert(param);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
res.add(new MethodParameter(pattern));
}
return res;
}
private boolean hasGeneric(Set<TargetGeneric> generics, GenericRefType type) {
@ -267,69 +303,127 @@ public class ASTToTargetAST {
return result;
}
private String encodeName(String name, ParameterList params) {
private String encodeName(String name, TargetMethod.Signature params) {
var res = new StringBuilder();
res.append(name);
res.append('$');
for (var param : params.getFormalparalist()) {
if (param instanceof RecordPattern rp) {
res.append(FunNGenerator.encodeType(convert(param.getType())));
for (var pattern : rp.getSubPattern()) {
res.append(FunNGenerator.encodeType(convert(pattern.getType())));
}
}
for (var param : params.parameters()) {
encodeName(param.pattern(), res);
}
return res.toString();
}
private List<TargetMethod> convert(ClassOrInterface clazz, List<Method> overloadedMethods) {
if (overloadedMethods.size() == 1) {
return convert(clazz, overloadedMethods.getFirst()).stream().map(m -> m.method()).toList();
}
var methods = new ArrayList<Method>();
for (var method : overloadedMethods) {
var newMethod = new Method(
method.modifier,
method.name,
//encodeName(method.name, method.getParameterList()),
method.getReturnType(),
method.getParameterList(),
method.block,
method.getGenerics(),
method.getOffset()
);
methods.add(newMethod);
}
// TODO Record overloading
/*var template = overloadedMethods.get(0);
var pParams = new ArrayList<Pattern>();
var i = 0;
for (var par : template.getParameterList()) {
pParams.add(switch (par) {
case RecordPattern rp -> new RecordPattern(rp.getSubPattern(), "par" + i, rp.getType(), new NullToken());
default -> par;
});
i++;
}
var params = new ParameterList(pParams, new NullToken());
var statements = new ArrayList<Statement>();
statements.add(new Return(makeRecordSwitch(template.getReturnType(), params, res), new NullToken()));
var block = new Block(statements, new NullToken());
var entryPoint = new Method(template.modifier, template.name, template.getReturnType(), params, block, template.getGenerics(), new NullToken());
res.add(entryPoint); // TODO*/
var res = new ArrayList<TargetMethod>();
for (var method : methods) {
var overloads = convert(clazz, method);
for (var m : overloads) {
var overload = m.method;
if (res.contains(overload)) throw new CodeGenException("Duplicate method found: " + overload.name() + " with signature " + overload.signature().getSignature());
res.add(overload);
private void encodeName(TargetPattern pattern, StringBuilder res) {
if (pattern instanceof TargetComplexPattern cp) {
res.append(FunNGenerator.encodeType(cp.type()));
for (var pat : cp.subPatterns()) {
encodeName(pat, res);
}
} else {
res.append(FunNGenerator.encodeType(pattern.type()));
}
}
private TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetExpression> params, List<TargetComplexPattern> patterns, List<TargetMethod> methods, TargetType classType) {
if (methods.isEmpty()) throw new DebugException("Couldn't find a candidate for switch overloading");
if (methods.size() == 1) {
var method = methods.getFirst();
TargetExpression caseBody = new TargetMethodCall(
method.signature().returnType(),
new TargetThis(classType),
params,
classType,
method.name(),
false, false, method.isPrivate()
);
if (method.signature().returnType() != null) {
caseBody = new TargetReturn(caseBody);
}
return caseBody;
}
var cases = new ArrayList<TargetSwitch.Case>();
var usedPatterns = new HashSet<TargetPattern>();
for (var method : methods) {
var patternsRec = new ArrayList<>(patterns);
TargetExpression expr = null;
var i = 0;
for (var param : method.signature().parameters()) {
if (param.pattern() instanceof TargetComplexPattern pat) {
if (i == offset) {
patternsRec.add(pat);
}
if (i > offset) {
// Find next pattern
var pattern = param.pattern();
expr = new TargetLocalVar(pattern.type(), pattern.name());
break;
}
i++;
}
}
var lastPattern = patternsRec.getLast();
if (usedPatterns.contains(lastPattern)) continue;
usedPatterns.add(lastPattern);
var candidates = methods.stream().filter(m -> {
var j = 0;
for (var param : m.signature().parameters()) {
if (j >= patternsRec.size()) return true;
if (param.pattern() instanceof TargetComplexPattern) {
if (!patternsRec.get(j).equals(param.pattern())) return false;
j++;
}
}
return true;
}).toList();
var caseBody = generatePatternOverloadsRec(offset + 1, expr, params, patternsRec, candidates, classType);
var body = new TargetBlock(List.of(caseBody));
var case_ = new TargetSwitch.Case(List.of(lastPattern), body);
cases.add(case_);
}
return new TargetSwitch(switchExpr, cases, null, true);
}
private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, List<TargetMethod> overloadedMethods) {
if (overloadedMethods.size() <= 1) return overloadedMethods;
// Check if we have a pattern as a parameter
var firstMethod = overloadedMethods.getFirst();
if (firstMethod.signature().parameters().stream().noneMatch(mp -> mp.pattern() instanceof TargetComplexPattern)) return overloadedMethods;
// Rename existing methods
var res = new ArrayList<TargetMethod>();
for (var method : overloadedMethods) {
var name = encodeName(method.name(), method.signature());
res.add(new TargetMethod(method.access(), name, method.block(), method.signature(), method.txSignature()));
}
var signatureParams = firstMethod.signature().parameters().stream().map(p -> {
if (p.pattern() instanceof TargetComplexPattern pat) {
return new MethodParameter(pat.type(), pat.name());
}
return p;
}).toList();
var parameters = firstMethod.signature().parameters().stream().map( p -> (TargetExpression) new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
//var patterns = List.of((TargetComplexPattern) firstMethod.signature().parameters().stream()
// .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern());
var firstPattern = firstMethod.signature().parameters().stream().filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern();
// Generate dispatch method
var classType = new TargetRefType(clazz.getClassName().getClassName());
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(firstPattern.type(), firstPattern.name()), parameters, List.of(), res, classType);
var block = new TargetBlock(List.of(stmt));
var signature = new TargetMethod.Signature(firstMethod.signature().generics(), signatureParams, firstMethod.signature().returnType());
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, signature, firstMethod.txSignature());
res.add(bridgeMethod);
return res;
}
@ -376,11 +470,16 @@ public class ASTToTargetAST {
record MethodWithTphs(TargetMethod method, List<SignaturePairTarget> args) {}
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {}
private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
generics = all.getFirst();
List<MethodWithTphs> result = new ArrayList<>();
this.currentMethod = method;
List<Signature> signatures = new ArrayList<>();
HashMap<TargetMethod.Signature, List<Generics>> collectedGenerics = new HashMap<>();
for (var s : all) {
generics = s;
var javaGenerics = this.generics.javaGenerics.generics(currentClass, method);
@ -404,8 +503,18 @@ public class ASTToTargetAST {
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), this.generics.txGenerics));
var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), javaSignature, txSignature);
signatures.add(new Signature(javaSignature, txSignature, generics));
var listOfGenerics = collectedGenerics.getOrDefault(javaSignature, new ArrayList<>());
listOfGenerics.add(generics);
collectedGenerics.put(javaSignature, listOfGenerics);
}
for (var signature : signatures) {
generics = signature.generics;
currentMethodOverloads = collectedGenerics.get(signature.java);
var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), signature.java, signature.tx);
var concreteParams = tphsInMethods.getOrDefault(method, new HashSet<>()).stream().map(sig -> new SignaturePairTarget(convert(sig.signature), convert(sig.parameter))).toList();
result.add(new MethodWithTphs(newMethod, concreteParams));
@ -418,7 +527,9 @@ public class ASTToTargetAST {
var list = new ArrayList<MethodParameter>();
for (var i = 0; i < paraList.getFormalparalist().size(); i++) {
var param = paraList.getParameterAt(i);
list.add(new MethodParameter((TargetPattern) convert(param)).withType(convert(superList.getParameterAt(i).getType(), generics)));
var pattern = (TargetPattern) convert(param);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
list.add(new MethodParameter(pattern).withType(convert(superList.getParameterAt(i).getType(), generics)));
}
return list;
}

View File

@ -134,8 +134,8 @@ public abstract class GenerateGenerics {
final Map<Method, Set<Pair>> familyOfMethods = new HashMap<>();
final Set<PairLT> simplifiedConstraints = new HashSet<>();
final Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
final Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
GenerateGenerics(ASTToTargetAST astToTargetAST, ResultSet constraints) {
this.astToTargetAST = astToTargetAST;
@ -154,6 +154,22 @@ public abstract class GenerateGenerics {
System.out.println("Simplified constraints: " + simplifiedConstraints);
}
/*public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {}
public GenericsState store() {
return new GenericsState(new HashMap<>(concreteTypes), new HashMap<>(equality));
}
public void restore(GenericsState state) {
this.concreteTypes = state.concreteTypes;
this.equality = state.equality;
}
public void addOverlay(TypePlaceholder from, RefTypeOrTPHOrWildcardOrGeneric to) {
if (to instanceof TypePlaceholder t) equality.put(from, t);
else if (to instanceof RefType t) concreteTypes.put(new TPH(from), t);
}*/
Set<TPH> findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) {
var result = new HashSet<TPH>();
if (type instanceof TypePlaceholder tph) {

View File

@ -1,24 +1,21 @@
package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.factory.PrimitiveMethodsGenerator;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetGeneric;
import de.dhbwstuttgart.target.tree.TargetMethod;
import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.*;
import javax.swing.text.html.Option;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class StatementToTargetExpression implements ASTVisitor {
@ -143,7 +140,11 @@ public class StatementToTargetExpression implements ASTVisitor {
@Override
public void visit(FieldVar fieldVar) {
result = new TargetFieldVar(converter.convert(fieldVar.getType()), converter.convert(fieldVar.receiver.getType()), fieldVar.isStatic, converter.convert(fieldVar.receiver), fieldVar.fieldVarName);
var isStatic = false;
var type = converter.convert(fieldVar.receiver.getType());
var clazz = converter.compiler.getClass(new JavaClassName(type.name()));
var field = clazz.getField(fieldVar.fieldVarName).orElseThrow();
result = new TargetFieldVar(converter.convert(fieldVar.getType()), type, Modifier.isStatic(field.modifier), converter.convert(fieldVar.receiver), fieldVar.fieldVarName);
}
@Override
@ -386,9 +387,94 @@ public class StatementToTargetExpression implements ASTVisitor {
result = new TargetTernary(converter.convert(ternary.getType()), converter.convert(ternary.cond), converter.convert(ternary.iftrue), converter.convert(ternary.iffalse));
}
record TypeVariants(RefTypeOrTPHOrWildcardOrGeneric in, List<RefTypeOrTPHOrWildcardOrGeneric> types) {}
private List<TypeVariants> extractAllPatterns(Pattern pattern) {
return switch (pattern) {
case GuardedPattern guarded -> extractAllPatterns(guarded.getNestedPattern());
case RecordPattern recordPattern -> recordPattern.getSubPattern().stream()
.map(this::extractAllPatterns)
.flatMap(List::stream).toList();
case FormalParameter param -> List.of(new TypeVariants(param.getType(), converter.findAllVariants(param.getType())));
default -> List.of();
};
}
record TypePair(RefTypeOrTPHOrWildcardOrGeneric in, RefTypeOrTPHOrWildcardOrGeneric out) {}
private void cartesianProduct(
List<TypeVariants> variants, int index,
List<RefTypeOrTPHOrWildcardOrGeneric> current,
List<List<RefTypeOrTPHOrWildcardOrGeneric>> result) {
if (index == variants.size()) {
result.add(new ArrayList<>(current));
return;
}
var currentSet = variants.get(index).types;
for (var element: currentSet) {
current.add(element);
cartesianProduct(variants, index + 1, current, result);
current.removeLast();
}
}
private List<List<TypePair>> cartesianProduct(List<TypeVariants> variants) {
var prod = new ArrayList<List<RefTypeOrTPHOrWildcardOrGeneric>>();
cartesianProduct(variants, 0, new ArrayList<>(), prod);
var res = new ArrayList<List<TypePair>>();
for (var list : prod) {
var l = new ArrayList<TypePair>();
for (var i = 0; i < list.size(); i++) {
l.add(new TypePair(variants.get(i).in, list.get(i)));
}
res.add(l);
}
return res;
}
@Override
public void visit(Switch switchStmt) {
var cases = switchStmt.getBlocks().stream().filter(s -> !s.isDefault()).map(converter::convert).toList();
var variants = converter.findAllVariants(switchStmt.getSwitch().getType());
var returns = converter.findAllVariants(switchStmt.getType());
var canBeOverloaded = variants.size() == 1 && returns.size() == 1;
var cases = switchStmt.getBlocks().stream().filter(s -> !s.isDefault()).map(case_ -> {
var overloads = new ArrayList<TargetSwitch.Case>();
if (canBeOverloaded) {
for (var label: case_.getLabels()) {
var product = cartesianProduct(extractAllPatterns(label.getPattern()));
for (var l : product) {
var oldGenerics = converter.generics;
// Set the generics to matching result set
for (var generics : converter.currentMethodOverloads) {
var java = generics.javaGenerics();
var equals = true;
for (var pair : l) {
if (!java.getType(pair.in).equals(pair.out)) {
equals = false; break;
}
}
if (equals) {
converter.generics = generics;
break;
}
}
overloads.add(converter.convert(case_));
converter.generics = oldGenerics;
}
}
} else {
overloads.add(converter.convert(case_));
}
return overloads;
}).flatMap(List::stream).toList();
TargetSwitch.Case default_ = null;
for (var block : switchStmt.getBlocks()) {
@ -482,6 +568,11 @@ public class StatementToTargetExpression implements ASTVisitor {
result = new TargetTypePattern(converter.convert(aPattern.getType()), aPattern.getName());
}
@Override
public void visit(LiteralPattern literalPattern) {
result = new TargetExpressionPattern(converter.convert(literalPattern.value));
}
@Override
public void visit(ExpressionPattern aPattern) {
result = converter.convert(aPattern.getExpression());

View File

@ -11,4 +11,8 @@ public record MethodParameter(TargetPattern pattern) {
public MethodParameter withType(TargetType type) {
return new MethodParameter(pattern.withType(type));
}
public MethodParameter withName(String name) {
return new MethodParameter(pattern.withName(name));
}
}

View File

@ -18,6 +18,19 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
public String getDescriptor() {
return TargetMethod.getDescriptor(returnType, parameters.stream().map(MethodParameter::pattern).map(TargetPattern::type).toArray(TargetType[]::new));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Signature signature = (Signature) o;
return Objects.equals(parameters, signature.parameters);
}
@Override
public int hashCode() {
return Objects.hash(parameters);
}
}
public static String getDescriptor(TargetType returnType, TargetType... parameters) {
@ -66,6 +79,10 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
return (access & Opcodes.ACC_STATIC) != 0;
}
public boolean isPrivate() {
return (access & Opcodes.ACC_PRIVATE) != 0;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof TargetMethod otherMethod)) return false;

View File

@ -9,4 +9,9 @@ public record TargetComplexPattern(TargetType type, String name, List<TargetPatt
public TargetComplexPattern withType(TargetType type) {
return new TargetComplexPattern(type, name, subPatterns);
}
@Override
public TargetComplexPattern withName(String name) {
return new TargetComplexPattern(type, name, subPatterns);
}
}

View File

@ -0,0 +1,20 @@
package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetExpressionPattern(TargetExpression expression) implements TargetPattern {
@Override
public TargetPattern withType(TargetType type) {
return this;
}
@Override
public TargetType type() {
return expression.type();
}
@Override
public TargetPattern withName(String name) {
return this;
}
}

View File

@ -12,4 +12,9 @@ public record TargetGuard(TargetPattern inner, TargetExpression expression) impl
public TargetType type() {
return inner.type();
}
@Override
public TargetGuard withName(String name) {
return new TargetGuard(inner.withName(name), expression);
}
}

View File

@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
public sealed interface TargetPattern extends TargetExpression permits TargetComplexPattern, TargetGuard, TargetTypePattern {
public sealed interface TargetPattern extends TargetExpression permits TargetComplexPattern, TargetExpressionPattern, TargetGuard, TargetTypePattern {
default String name() {
return null;
}
@ -10,4 +10,6 @@ public sealed interface TargetPattern extends TargetExpression permits TargetCom
TargetPattern withType(TargetType type);
TargetType type();
TargetPattern withName(String name);
}

View File

@ -7,4 +7,9 @@ public record TargetTypePattern(TargetType type, String name) implements TargetP
public TargetTypePattern withType(TargetType type) {
return new TargetTypePattern(type, name);
}
@Override
public TargetTypePattern withName(String name) {
return new TargetTypePattern(type, name);
}
}

View File

@ -13,7 +13,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TypeInsertPlacer extends AbstractASTWalker{
public class TypeInsertPlacer extends AbstractASTWalker {
Set<TypeInsert> inserts = new HashSet<>();
private ResultSet withResults;
String pkgName;

View File

@ -1,6 +1,8 @@
package de.dhbwstuttgart.typeinference.typeAlgo;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.parser.SourceLoc;
import de.dhbwstuttgart.parser.antlr.Java17Parser;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
@ -9,7 +11,10 @@ import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceBlockInformation;
import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceInformation;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import de.dhbwstuttgart.typeinference.constraints.Pair;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import de.dhbwstuttgart.util.BiRelation;
import org.antlr.v4.runtime.Token;
import java.util.*;
@ -79,8 +84,46 @@ public class TYPE {
if(m.block == null)return new ConstraintSet(); //Abstrakte Methoden generieren keine Constraints
TypeInferenceBlockInformation blockInfo = new TypeInferenceBlockInformation(info.getAvailableClasses(), currentClass, m);
TYPEStmt methodScope = new TYPEStmt(blockInfo);
ConstraintSet constraintSet = new ConstraintSet();
m.getParameterList().getFormalparalist().forEach(el -> {
if(el instanceof RecordPattern){
constraintSet.addAll(addRecursiveParameterConstraints((RecordPattern) el, blockInfo));
}
});
m.block.accept(methodScope);
return methodScope.getConstraints();
constraintSet.addAll(methodScope.getConstraints());
return constraintSet;
}
public ConstraintSet addRecursiveParameterConstraints(RecordPattern recordPattern, TypeInferenceBlockInformation blockInformation){
ConstraintSet constraintSet = new ConstraintSet();
var subPatternList = recordPattern.getSubPattern();
int counter = 0;
for(Pattern el : subPatternList){
if(el instanceof RecordPattern){
constraintSet.addAll(addRecursiveParameterConstraints((RecordPattern) el, blockInformation));
}else{
var allClasses = blockInformation.getAvailableClasses();
RefTypeOrTPHOrWildcardOrGeneric type;
for (ClassOrInterface allClass : allClasses) {
var typename = recordPattern.getType().toString();
var className = allClass.getClassName().getClassName();
if(className.equals(typename)){
type = allClass.getConstructors().get(0).getParameterList().getParameterAt(counter).getType();
constraintSet.addUndConstraint(new Pair(el.getType(), type, PairOperator.SMALLERDOT, new SourceLoc(blockInformation.getCurrentClass().getFileName(), el.getOffset().getLine())));
}
}
}
counter++;
}
return constraintSet;
}
private ConstraintSet getConstraintsConstructor(Constructor m, TypeInferenceInformation info, ClassOrInterface currentClass) {

View File

@ -117,6 +117,8 @@ public class TYPEStmt implements StatementVisitor {
public void visit(FieldVar fieldVar) {
fieldVar.receiver.accept(this);
Set<Constraint> oderConstraints = new HashSet<>();
for (FieldAssumption fieldAssumption : info.getFields(fieldVar.fieldVarName)) {
Constraint constraint = new Constraint();
GenericsResolver resolver = getResolverInstance();
@ -128,7 +130,6 @@ public class TYPEStmt implements StatementVisitor {
throw new TypeinferenceException("Kein Feld " + fieldVar.fieldVarName + " gefunden", fieldVar.getOffset());
constraintsSet.addOderConstraint(oderConstraints);
}
@Override
public void visit(ForStmt forStmt) {
forStmt.initializer.forEach(s -> s.accept(this));
@ -840,7 +841,7 @@ public class TYPEStmt implements StatementVisitor {
for (var child : switchStmt.getBlocks()) {
for (var label : child.getLabels()) {
if (label.getPattern() == null) {
System.out.println("DefaultCase");
//System.out.println("DefaultCase");
} else {
constraintsSet.addUndConstraint(
new Pair(
@ -861,7 +862,7 @@ public class TYPEStmt implements StatementVisitor {
for (var child : switchStmt.getBlocks()) {
for (var label : child.getLabels()) {
if (label.getPattern() == null) {
System.out.println("DefaultCase");
constraintsSet.addUndConstraint(new Pair(new RefType(new JavaClassName("java.lang.Object"), new NullToken()), switchStmt.getSwitch().getType(), PairOperator.SMALLERDOT, loc(label.getOffset())));
}
if (label.getPattern() instanceof RecordPattern) {
RecordPattern recordPattern = (RecordPattern) label.getPattern();
@ -909,10 +910,11 @@ public class TYPEStmt implements StatementVisitor {
for (var subPattern : pattern.getSubPattern()) {
for (Constructor con : constructors) {
System.out.println("----------------------\n" + subPattern.getType() + " | " + con.getParameterList().getParameterAt(counter).getType() + "\n----------------------\n");
//System.out.println("----------------------\n" + subPattern.getType() + " | " + con.getParameterList().getParameterAt(counter).getType() + "\n----------------------\n");
constraintsSet.addUndConstraint(new Pair(subPattern.getType(), con.getParameterList().getParameterAt(counter).getType(), PairOperator.SMALLERDOT, loc(con.getParameterList().getParameterAt(counter).getOffset())));
}
if (subPattern instanceof RecordPattern) recursivelyAddRecordConstraints((RecordPattern) subPattern);
else if (subPattern instanceof LiteralPattern lp) lp.value.accept(this);
counter++;
}
}

View File

@ -728,7 +728,12 @@ public class TestComplete {
assertEquals(res, expected);
}
@Test
public void testSwitchRecordLiteral() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "PatternMatchingLiteralStyle.jav");
var clazz = classFiles.get("SwitchOverload");
}
@Test
public void testSwitchCaseHeritageDetection() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "SwitchCaseHeritageDetection.jav");
@ -777,6 +782,24 @@ public class TestComplete {
assertEquals(swtch.invoke(instance, listWithOneElement), 10);
}
@Test
public void testSwitchNestedValue() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "SwitchNestedValue.jav");
var clazz = classFiles.get("SwitchNestedValue");
var rec = classFiles.get("R");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("main", rec);
var ctor = rec.getDeclaredConstructor(Integer.class);
var r1 = ctor.newInstance(10);
var r2 = ctor.newInstance(20);
var r3 = ctor.newInstance(30);
assertEquals(m.invoke(instance, r1), 1);
assertEquals(m.invoke(instance, r2), 2);
assertEquals(m.invoke(instance, r3), 3);
}
@Test
public void testGenericRecordSwitchCase() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "GenericRecordSwitchCase.jav");
@ -784,11 +807,11 @@ public class TestComplete {
var clazz = classFiles.get("GenericRecordSwitchCase");
var instance = clazz.getDeclaredConstructor().newInstance();
var swtch = clazz.getDeclaredMethod("main", Object.class);
var linkedElem = classFiles.get("LinkedElem");
var listInterface = classFiles.get("List");
var elem = classFiles.get("Elem");
var swtch = clazz.getDeclaredMethod("main", linkedElem);
var constructorLinkedElem = linkedElem.getConstructor(Object.class, listInterface);
@ -833,7 +856,6 @@ public class TestComplete {
var instance = clazz.getDeclaredConstructor().newInstance();
}
@Ignore("Not implemented")
@Test
public void testOverloadPattern() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "OverloadPattern.jav");
@ -841,12 +863,91 @@ public class TestComplete {
var rec = classFiles.get("Point");
var instance = clazz.getDeclaredConstructor().newInstance();
var m1 = clazz.getDeclaredMethod("m", rec);
var m2 = clazz.getDeclaredMethod("m", Integer.class);
var m1 = clazz.getDeclaredMethod("m", rec, rec);
var m2 = clazz.getDeclaredMethod("m", rec);
var m3 = clazz.getDeclaredMethod("m", Integer.class);
var pt = rec.getDeclaredConstructor(Number.class, Number.class).newInstance(10, 20);
assertEquals(m1.invoke(instance, pt), 30);
assertEquals(m2.invoke(instance, 10), 10);
var ptInt = rec.getDeclaredConstructor(Number.class, Number.class).newInstance(1, 2);
var ptFlt = rec.getDeclaredConstructor(Number.class, Number.class).newInstance(1f, 2f);
assertEquals(m1.invoke(instance, ptInt, ptFlt), 1);
assertEquals(m1.invoke(instance, ptInt, ptInt), 2);
assertEquals(m1.invoke(instance, ptFlt, ptInt), 3);
assertEquals(m1.invoke(instance, ptFlt, ptFlt), 4);
assertEquals(m2.invoke(instance, ptInt), 5);
assertEquals(m2.invoke(instance, ptFlt), 6);
assertEquals(m3.invoke(instance, 10), 10);
}
@Test
public void testOverloadNestedPattern() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "OverloadNestedPattern.jav");
var clazz = classFiles.get("OverloadNestedPattern");
var rec = classFiles.get("R");
var instance = clazz.getDeclaredConstructor().newInstance();
var ctor = rec.getDeclaredConstructor(Object.class);
var r1 = ctor.newInstance(1);
var r2 = ctor.newInstance(r1);
var m = clazz.getDeclaredMethod("m", rec, rec);
assertEquals(m.invoke(instance, r2, r1), 1);
var r3 = ctor.newInstance(2f);
var r4 = ctor.newInstance(r3);
assertEquals(m.invoke(instance, r4, r3), 2);
assertEquals(m.invoke(instance, r1, r1), 3);
}
//@Ignore("Not implemented")
@Test
public void testPatternMatchingHaskellStyle() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "PatternMatchingHaskellStyle.jav");
var clazz = classFiles.get("PatternMatchingHaskellStyle");
var R2 = classFiles.get("Elem");
var R = classFiles.get("LinkedElem");
var I = classFiles.get("List");
var rctor = R.getDeclaredConstructor(Object.class, I);
var r2ctor = R2.getDeclaredConstructor(Object.class);
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("append$LLinkedElem$_$LTPH$_$LLinkedElem$_$LTPH$_$", R, Object.class);
var x = rctor.newInstance(rctor.newInstance(0, rctor.newInstance(0, r2ctor.newInstance(0))), r2ctor.newInstance(0));
var y = rctor.newInstance(r2ctor.newInstance(0), r2ctor.newInstance(0));
assertEquals(m.invoke(instance, x, y), null);
assertEquals(m.invoke(instance, y, y), null);
}
@Test
public void testPatternMatchingListAppend() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "PatternMatchingListAppend.jav");
var clazz = classFiles.get("PatternMatchingListAppend");
var instance = clazz.getDeclaredConstructor().newInstance();
}
@Test
public void testOverloadSwitch() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "SwitchOverload.jav");
var clazz = classFiles.get("SwitchOverload");
var R = classFiles.get("R");
var rctor = R.getDeclaredConstructor(Number.class);
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m", R, Integer.class);
var x = rctor.newInstance(10);
var d = rctor.newInstance(20.0);
assertEquals(m.invoke(instance, x, 0), 50);
assertEquals(m.invoke(instance, d, 0), 40.0);
}
@Test
@ -1066,6 +1167,15 @@ public class TestComplete {
var instance = clazz.getDeclaredConstructor().newInstance();
}
@Test
public void testAssign() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Assign.jav");
var clazz = classFiles.get("Assign");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m");
assertEquals(m.invoke(instance), 20);
}
@Test
public void testBug122() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug122.jav");