Make switches work with set types
This commit is contained in:
parent
be5591f7dc
commit
762d344e42
@ -5,10 +5,10 @@ import java.lang.Float;
|
|||||||
record Rec(Integer a, Object b) {}
|
record Rec(Integer a, Object b) {}
|
||||||
|
|
||||||
public class Switch {
|
public class Switch {
|
||||||
main(Object o) {
|
Integer main(Object o) {
|
||||||
return switch (o) {
|
return switch (o) {
|
||||||
case Rec(Integer a, Integer b) -> { yield a + b; }
|
case Rec(Integer a, Integer b) -> { yield a + b; }
|
||||||
case Rec(Integer a, Float b) -> { yield a * b; }
|
case Rec(Integer a, Float b) -> { yield a + 10; }
|
||||||
case Rec(Integer a, Rec(Integer b, Integer c)) -> { yield a + b + c; }
|
case Rec(Integer a, Rec(Integer b, Integer c)) -> { yield a + b + c; }
|
||||||
case Integer i -> { yield i; }
|
case Integer i -> { yield i; }
|
||||||
default -> { yield 0; }
|
default -> { yield 0; }
|
||||||
|
@ -28,7 +28,12 @@ public class Codegen {
|
|||||||
public Codegen(TargetStructure clazz, JavaTXCompiler compiler) {
|
public Codegen(TargetStructure clazz, JavaTXCompiler compiler) {
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
this.className = clazz.qualifiedName();
|
this.className = clazz.qualifiedName();
|
||||||
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
|
||||||
|
@Override
|
||||||
|
protected ClassLoader getClassLoader() {
|
||||||
|
return compiler.getClassLoader();
|
||||||
|
}
|
||||||
|
};
|
||||||
this.compiler = compiler;
|
this.compiler = compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1084,7 +1089,7 @@ public class Codegen {
|
|||||||
private void generateEnhancedSwitch(State state, TargetSwitch aSwitch) {
|
private void generateEnhancedSwitch(State state, TargetSwitch aSwitch) {
|
||||||
var mv = state.mv;
|
var mv = state.mv;
|
||||||
generate(state, aSwitch.expr());
|
generate(state, aSwitch.expr());
|
||||||
var tmp = state.localCounter++;
|
var tmp = state.localCounter;
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
mv.visitVarInsn(ASTORE, tmp);
|
mv.visitVarInsn(ASTORE, tmp);
|
||||||
|
|
||||||
@ -1148,12 +1153,12 @@ public class Codegen {
|
|||||||
|
|
||||||
if (cse.labels().size() == 1) {
|
if (cse.labels().size() == 1) {
|
||||||
var label = cse.labels().get(0);
|
var label = cse.labels().get(0);
|
||||||
if (label instanceof Guard gd){
|
if (label instanceof Guard gd) {
|
||||||
state.mv.visitVarInsn(ALOAD, tmp);
|
state.mv.visitVarInsn(ALOAD, tmp);
|
||||||
bindPattern(state, aSwitch.expr().type(), gd.inner(), start);
|
bindPattern(state, aSwitch.expr().type(), gd.inner(), start, i, 1);
|
||||||
} else if (label instanceof TargetPattern pat) {
|
} else if (label instanceof TargetPattern pat) {
|
||||||
state.mv.visitVarInsn(ALOAD, tmp);
|
state.mv.visitVarInsn(ALOAD, tmp);
|
||||||
bindPattern(state, aSwitch.expr().type(), pat, start);
|
bindPattern(state, aSwitch.expr().type(), pat, start, i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (label instanceof Guard gd) {
|
if (label instanceof Guard gd) {
|
||||||
@ -1194,30 +1199,51 @@ public class Codegen {
|
|||||||
state.exitScope();
|
state.exitScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindPattern(State state, TargetType type, TargetPattern pat, Label start) {
|
private void bindPattern(State state, TargetType type, TargetPattern pat, Label start, int index, int depth) {
|
||||||
|
if (pat.type() instanceof TargetPrimitiveType)
|
||||||
|
boxPrimitive(state, pat.type());
|
||||||
|
|
||||||
|
state.mv.visitInsn(DUP);
|
||||||
|
state.mv.visitTypeInsn(INSTANCEOF, pat.type().getInternalName());
|
||||||
|
|
||||||
|
var cont = new Label();
|
||||||
|
state.mv.visitJumpInsn(IFNE, cont);
|
||||||
|
for (var i = 0; i < depth; 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);
|
||||||
|
|
||||||
|
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
|
||||||
|
|
||||||
if (pat instanceof SimplePattern sp) {
|
if (pat instanceof SimplePattern sp) {
|
||||||
var local = state.createVariable(sp.name(), sp.type());
|
var local = state.createVariable(sp.name(), sp.type());
|
||||||
convertTo(state, type, sp.type());
|
|
||||||
boxPrimitive(state, sp.type());
|
|
||||||
state.mv.visitVarInsn(ASTORE, local.index);
|
state.mv.visitVarInsn(ASTORE, local.index);
|
||||||
} else if (pat instanceof ComplexPattern cp) {
|
} else if (pat instanceof ComplexPattern cp) {
|
||||||
convertTo(state, type, cp.type());
|
if (cp.name() != null) {
|
||||||
boxPrimitive(state, cp.type());
|
state.mv.visitInsn(DUP);
|
||||||
|
var local = state.createVariable(cp.name(), cp.type());
|
||||||
|
state.mv.visitVarInsn(ASTORE, local.index);
|
||||||
|
}
|
||||||
|
|
||||||
var clazz = findClass(new JavaClassName(cp.type().name()));
|
var clazz = findClass(new JavaClassName(cp.type().name()));
|
||||||
if (clazz == null) throw new CodeGenException("Class definition for '" + cp.type().name() + "' not found");
|
if (clazz == null) throw new CodeGenException("Class definition for '" + cp.type().name() + "' not found");
|
||||||
// TODO Check if class is a Record
|
// TODO Check if class is a Record
|
||||||
|
|
||||||
for (var i = 0; i < cp.subPatterns().size(); i++) {
|
for (var i = 0; i < cp.subPatterns().size(); i++) {
|
||||||
|
state.mv.visitInsn(DUP);
|
||||||
|
|
||||||
var subPattern = cp.subPatterns().get(i);
|
var subPattern = cp.subPatterns().get(i);
|
||||||
if (i >= clazz.getFieldDecl().size())
|
if (i >= clazz.getFieldDecl().size())
|
||||||
throw new CodeGenException("Couldn't find suitable field accessor for '" + cp.type().name() + "'");
|
throw new CodeGenException("Couldn't find suitable field accessor for '" + cp.type().name() + "'");
|
||||||
var field = clazz.getFieldDecl().get(i);
|
var field = clazz.getFieldDecl().get(i);
|
||||||
var fieldType = new TargetRefType(((RefType) field.getType()).getName().toString());
|
var fieldType = new TargetRefType(((RefType) field.getType()).getName().toString());
|
||||||
state.mv.visitMethodInsn(INVOKEDYNAMIC, cp.type().getInternalName(), field.getName(), "()" + fieldType.toDescriptor(), false);
|
state.mv.visitMethodInsn(INVOKEVIRTUAL, cp.type().getInternalName(), field.getName(), "()" + fieldType.toDescriptor(), false);
|
||||||
convertTo(state, fieldType, subPattern.type());
|
bindPattern(state, subPattern.type(), subPattern, start, index, depth + 1);
|
||||||
bindPattern(state, subPattern.type(), subPattern, start);
|
|
||||||
}
|
}
|
||||||
|
state.mv.visitInsn(POP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,10 @@ public class JavaTXCompiler {
|
|||||||
public volatile UnifyTaskModel usedTasks = new UnifyTaskModel();
|
public volatile UnifyTaskModel usedTasks = new UnifyTaskModel();
|
||||||
private final DirectoryClassLoader classLoader;
|
private final DirectoryClassLoader classLoader;
|
||||||
|
|
||||||
|
public DirectoryClassLoader getClassLoader() {
|
||||||
|
return classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
public JavaTXCompiler(File sourceFile) throws IOException, ClassNotFoundException {
|
public JavaTXCompiler(File sourceFile) throws IOException, ClassNotFoundException {
|
||||||
this(Arrays.asList(sourceFile), null);
|
this(Arrays.asList(sourceFile), null);
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ public class StatementGenerator {
|
|||||||
for (SwitchBlockStatementGroupContext blockstmt : stmt.switchBlockStatementGroup()) {
|
for (SwitchBlockStatementGroupContext blockstmt : stmt.switchBlockStatementGroup()) {
|
||||||
switchBlocks.add(convert(blockstmt));
|
switchBlocks.add(convert(blockstmt));
|
||||||
}
|
}
|
||||||
return new Switch(switched, switchBlocks, switched.getType(), true, stmt.getStart());
|
return new Switch(switched, switchBlocks, TypePlaceholder.fresh(switched.getOffset()), true, stmt.getStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Um switchExpressions als Statement zu behandeln
|
// Um switchExpressions als Statement zu behandeln
|
||||||
@ -474,7 +474,10 @@ public class StatementGenerator {
|
|||||||
switch (pPattern) {
|
switch (pPattern) {
|
||||||
case TPatternContext tPattern:
|
case TPatternContext tPattern:
|
||||||
TypePatternContext typePattern = tPattern.typePattern();
|
TypePatternContext typePattern = tPattern.typePattern();
|
||||||
return new Pattern(typePattern.identifier().getText(), TypeGenerator.convert(typePattern.typeType(), reg, generics), typePattern.getStart());
|
var text = typePattern.identifier().getText();
|
||||||
|
var type = TypeGenerator.convert(typePattern.typeType(), reg, generics);
|
||||||
|
localVars.put(text, type);
|
||||||
|
return new Pattern(text, type, typePattern.getStart());
|
||||||
case RPatternContext rPattern:
|
case RPatternContext rPattern:
|
||||||
RecordPatternContext recordPattern = rPattern.recordPattern();
|
RecordPatternContext recordPattern = rPattern.recordPattern();
|
||||||
return convert(recordPattern);
|
return convert(recordPattern);
|
||||||
@ -490,7 +493,10 @@ public class StatementGenerator {
|
|||||||
return (Pattern) convert(patternCtx);
|
return (Pattern) convert(patternCtx);
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
IdentifierContext identifierCtx = recordPatternCtx.identifier();
|
IdentifierContext identifierCtx = recordPatternCtx.identifier();
|
||||||
return new RecordPattern(subPattern, (identifierCtx != null) ? identifierCtx.getText() : null, TypeGenerator.convert(recordPatternCtx.typeType(), reg, generics), recordPatternCtx.getStart());
|
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
|
||||||
|
var type = TypeGenerator.convert(recordPatternCtx.typeType(), reg, generics);
|
||||||
|
if (text != null) localVars.put(text, type);
|
||||||
|
return new RecordPattern(subPattern, text, type, recordPatternCtx.getStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement convert(Java17Parser.WhileloopContext stmt) {
|
private Statement convert(Java17Parser.WhileloopContext stmt) {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
//PL 2018-12-19: Merge chekcen
|
//PL 2018-12-19: Merge chekcen
|
||||||
package de.dhbwstuttgart.typeinference.typeAlgo;
|
package de.dhbwstuttgart.typeinference.typeAlgo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
||||||
@ -78,6 +75,7 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
|
|
||||||
private final TypeInferenceBlockInformation info;
|
private final TypeInferenceBlockInformation info;
|
||||||
private final ConstraintSet constraintsSet = new ConstraintSet();
|
private final ConstraintSet constraintsSet = new ConstraintSet();
|
||||||
|
private final Stack<Switch> switchStack = new Stack<>();
|
||||||
|
|
||||||
public TYPEStmt(TypeInferenceBlockInformation info) {
|
public TYPEStmt(TypeInferenceBlockInformation info) {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
@ -735,12 +733,19 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Switch switchStmt) {
|
public void visit(Switch switchStmt) {
|
||||||
// TODO Auto-generated method stub
|
switchStack.push(switchStmt);
|
||||||
|
for (var child : switchStmt.getBlocks()) {
|
||||||
|
child.accept(this);
|
||||||
|
constraintsSet.addUndConstraint(new Pair(child.getType(), switchStmt.getType(), PairOperator.SMALLERDOT));
|
||||||
|
}
|
||||||
|
switchStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(SwitchBlock switchBlock) {
|
public void visit(SwitchBlock switchBlock) {
|
||||||
// TODO Auto-generated method stub
|
for (var stmt : switchBlock.statements) {
|
||||||
|
stmt.accept(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -750,6 +755,8 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Yield aYield) {
|
public void visit(Yield aYield) {
|
||||||
|
aYield.retexpr.accept(this);
|
||||||
|
constraintsSet.addUndConstraint(new Pair(aYield.getType(), switchStack.peek().getType(), PairOperator.SMALLERDOT));
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,5 +656,18 @@ public class TestComplete {
|
|||||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Switch.jav");
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Switch.jav");
|
||||||
var clazz = classFiles.get("Switch");
|
var clazz = classFiles.get("Switch");
|
||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
var swtch = clazz.getDeclaredMethod("main", Object.class);
|
||||||
|
|
||||||
|
var record = classFiles.get("Rec");
|
||||||
|
var ctor = record.getDeclaredConstructor(Integer.class, Object.class);
|
||||||
|
var r1 = ctor.newInstance(10, 20);
|
||||||
|
var r2 = ctor.newInstance(10, 20f);
|
||||||
|
var r3 = ctor.newInstance(10, r1);
|
||||||
|
|
||||||
|
assertEquals(swtch.invoke(instance, r1), 30);
|
||||||
|
assertEquals(swtch.invoke(instance, r2), 20);
|
||||||
|
assertEquals(swtch.invoke(instance, r3), 40);
|
||||||
|
assertEquals(swtch.invoke(instance, 50), 50);
|
||||||
|
assertEquals(swtch.invoke(instance, "Some string"), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user