forked from JavaTX/JavaCompilerCore
Allow the first patterns
This commit is contained in:
parent
be55d661cb
commit
4f3164a48a
@ -717,10 +717,9 @@ switchLabelCase
|
|||||||
| DEFAULT (ARROW | COLON) #labeledRuleDefault
|
| DEFAULT (ARROW | COLON) #labeledRuleDefault
|
||||||
;
|
;
|
||||||
|
|
||||||
// Java17
|
// Java20
|
||||||
guardedPattern
|
guardedPattern
|
||||||
: variableModifier* typeType annotation* identifier ('&&' expression)*
|
: primaryPattern WITH expression
|
||||||
| guardedPattern '&&' expression
|
|
||||||
;
|
;
|
||||||
|
|
||||||
// Java17
|
// Java17
|
||||||
|
@ -5,6 +5,7 @@ import de.dhbwstuttgart.syntaxtree.statement.Break;
|
|||||||
import de.dhbwstuttgart.target.tree.*;
|
import de.dhbwstuttgart.target.tree.*;
|
||||||
import de.dhbwstuttgart.target.tree.expression.*;
|
import de.dhbwstuttgart.target.tree.expression.*;
|
||||||
import de.dhbwstuttgart.target.tree.type.*;
|
import de.dhbwstuttgart.target.tree.type.*;
|
||||||
|
import org.antlr.v4.codegen.Target;
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
import java.lang.invoke.CallSite;
|
import java.lang.invoke.CallSite;
|
||||||
@ -71,6 +72,7 @@ public class Codegen {
|
|||||||
TargetType returnType;
|
TargetType returnType;
|
||||||
|
|
||||||
Stack<BreakEnv> breakStack = new Stack<>();
|
Stack<BreakEnv> breakStack = new Stack<>();
|
||||||
|
Stack<Integer> switchResultValue = new Stack<>();
|
||||||
|
|
||||||
State(TargetType returnType, MethodVisitor mv, int localCounter) {
|
State(TargetType returnType, MethodVisitor mv, int localCounter) {
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
@ -92,6 +94,13 @@ public class Codegen {
|
|||||||
localCounter += 1;
|
localCounter += 1;
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pushSwitch() {
|
||||||
|
switchResultValue.push(this.localCounter++);
|
||||||
|
}
|
||||||
|
void popSwitch() {
|
||||||
|
switchResultValue.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void popValue(State state, TargetType type) {
|
private void popValue(State state, TargetType type) {
|
||||||
@ -582,119 +591,118 @@ public class Codegen {
|
|||||||
private void generateUnaryOp(State state, TargetUnaryOp op) {
|
private void generateUnaryOp(State state, TargetUnaryOp op) {
|
||||||
var mv = state.mv;
|
var mv = state.mv;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case TargetUnaryOp.Add add:
|
case TargetUnaryOp.Add add ->
|
||||||
// This literally does nothing
|
// This literally does nothing
|
||||||
generate(state, add.expr());
|
generate(state, add.expr());
|
||||||
break;
|
case TargetUnaryOp.Negate negate -> {
|
||||||
case TargetUnaryOp.Negate negate:
|
generate(state, negate.expr());
|
||||||
generate(state, negate.expr());
|
if (negate.type().equals(TargetType.Double))
|
||||||
if (negate.type().equals(TargetType.Double))
|
mv.visitInsn(DNEG);
|
||||||
mv.visitInsn(DNEG);
|
else if (negate.type().equals(TargetType.Float))
|
||||||
else if (negate.type().equals(TargetType.Float))
|
mv.visitInsn(FNEG);
|
||||||
mv.visitInsn(FNEG);
|
else if (negate.type().equals(TargetType.Long))
|
||||||
else if (negate.type().equals(TargetType.Long))
|
mv.visitInsn(LNEG);
|
||||||
mv.visitInsn(LNEG);
|
else
|
||||||
else
|
mv.visitInsn(INEG);
|
||||||
mv.visitInsn(INEG);
|
|
||||||
break;
|
|
||||||
case TargetUnaryOp.Not not:
|
|
||||||
generate(state, not.expr());
|
|
||||||
if (not.type().equals(TargetType.Long)) {
|
|
||||||
mv.visitLdcInsn(-1L);
|
|
||||||
mv.visitInsn(LXOR);
|
|
||||||
} else {
|
|
||||||
mv.visitInsn(ICONST_M1);
|
|
||||||
mv.visitInsn(IXOR);
|
|
||||||
}
|
}
|
||||||
break;
|
case TargetUnaryOp.Not not -> {
|
||||||
case TargetUnaryOp.PreIncrement preIncrement:
|
generate(state, not.expr());
|
||||||
generate(state, preIncrement.expr());
|
if (not.type().equals(TargetType.Long)) {
|
||||||
if (preIncrement.type().equals(TargetType.Float)) {
|
mv.visitLdcInsn(-1L);
|
||||||
mv.visitLdcInsn(1F);
|
mv.visitInsn(LXOR);
|
||||||
mv.visitInsn(FADD);
|
} else {
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(ICONST_M1);
|
||||||
} else if (preIncrement.type().equals(TargetType.Double)) {
|
mv.visitInsn(IXOR);
|
||||||
mv.visitLdcInsn(1D);
|
}
|
||||||
mv.visitInsn(DADD);
|
|
||||||
mv.visitInsn(DUP2);
|
|
||||||
} else if (preIncrement.type().equals(TargetType.Long)) {
|
|
||||||
mv.visitLdcInsn(1L);
|
|
||||||
mv.visitInsn(LADD);
|
|
||||||
mv.visitInsn(DUP2);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(1);
|
|
||||||
mv.visitInsn(IADD);
|
|
||||||
mv.visitInsn(DUP);
|
|
||||||
}
|
}
|
||||||
boxPrimitive(state, preIncrement.type());
|
case TargetUnaryOp.PreIncrement preIncrement -> {
|
||||||
afterIncDec(state, preIncrement);
|
generate(state, preIncrement.expr());
|
||||||
break;
|
if (preIncrement.type().equals(TargetType.Float)) {
|
||||||
case TargetUnaryOp.PreDecrement preDecrement:
|
mv.visitLdcInsn(1F);
|
||||||
generate(state, preDecrement.expr());
|
mv.visitInsn(FADD);
|
||||||
if (preDecrement.type().equals(TargetType.Float)) {
|
mv.visitInsn(DUP);
|
||||||
mv.visitLdcInsn(1F);
|
} else if (preIncrement.type().equals(TargetType.Double)) {
|
||||||
mv.visitInsn(FSUB);
|
mv.visitLdcInsn(1D);
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DADD);
|
||||||
} else if (preDecrement.type().equals(TargetType.Double)) {
|
mv.visitInsn(DUP2);
|
||||||
mv.visitLdcInsn(1D);
|
} else if (preIncrement.type().equals(TargetType.Long)) {
|
||||||
mv.visitInsn(DSUB);
|
mv.visitLdcInsn(1L);
|
||||||
mv.visitInsn(DUP2);
|
mv.visitInsn(LADD);
|
||||||
} else if (preDecrement.type().equals(TargetType.Long)) {
|
mv.visitInsn(DUP2);
|
||||||
mv.visitLdcInsn(1L);
|
} else {
|
||||||
mv.visitInsn(LSUB);
|
mv.visitLdcInsn(1);
|
||||||
mv.visitInsn(DUP2);
|
mv.visitInsn(IADD);
|
||||||
} else {
|
mv.visitInsn(DUP);
|
||||||
mv.visitLdcInsn(1);
|
}
|
||||||
mv.visitInsn(ISUB);
|
boxPrimitive(state, preIncrement.type());
|
||||||
mv.visitInsn(DUP);
|
afterIncDec(state, preIncrement);
|
||||||
}
|
}
|
||||||
boxPrimitive(state, preDecrement.type());
|
case TargetUnaryOp.PreDecrement preDecrement -> {
|
||||||
afterIncDec(state, preDecrement);
|
generate(state, preDecrement.expr());
|
||||||
break;
|
if (preDecrement.type().equals(TargetType.Float)) {
|
||||||
case TargetUnaryOp.PostIncrement postIncrement:
|
mv.visitLdcInsn(1F);
|
||||||
generate(state, postIncrement.expr());
|
mv.visitInsn(FSUB);
|
||||||
if (postIncrement.type().equals(TargetType.Float)) {
|
mv.visitInsn(DUP);
|
||||||
mv.visitInsn(DUP);
|
} else if (preDecrement.type().equals(TargetType.Double)) {
|
||||||
mv.visitLdcInsn(1F);
|
mv.visitLdcInsn(1D);
|
||||||
mv.visitInsn(FADD);
|
mv.visitInsn(DSUB);
|
||||||
} else if (postIncrement.type().equals(TargetType.Double)) {
|
mv.visitInsn(DUP2);
|
||||||
mv.visitInsn(DUP2);
|
} else if (preDecrement.type().equals(TargetType.Long)) {
|
||||||
mv.visitLdcInsn(1D);
|
mv.visitLdcInsn(1L);
|
||||||
mv.visitInsn(DADD);
|
mv.visitInsn(LSUB);
|
||||||
} else if (postIncrement.type().equals(TargetType.Long)) {
|
mv.visitInsn(DUP2);
|
||||||
mv.visitInsn(DUP2);
|
} else {
|
||||||
mv.visitLdcInsn(1L);
|
mv.visitLdcInsn(1);
|
||||||
mv.visitInsn(LADD);
|
mv.visitInsn(ISUB);
|
||||||
} else {
|
mv.visitInsn(DUP);
|
||||||
mv.visitInsn(DUP);
|
}
|
||||||
mv.visitLdcInsn(1);
|
boxPrimitive(state, preDecrement.type());
|
||||||
mv.visitInsn(IADD);
|
afterIncDec(state, preDecrement);
|
||||||
}
|
}
|
||||||
boxPrimitive(state, postIncrement.type());
|
case TargetUnaryOp.PostIncrement postIncrement -> {
|
||||||
afterIncDec(state, postIncrement);
|
generate(state, postIncrement.expr());
|
||||||
break;
|
if (postIncrement.type().equals(TargetType.Float)) {
|
||||||
case TargetUnaryOp.PostDecrement postDecrement:
|
mv.visitInsn(DUP);
|
||||||
generate(state, postDecrement.expr());
|
mv.visitLdcInsn(1F);
|
||||||
if (postDecrement.type().equals(TargetType.Float)) {
|
mv.visitInsn(FADD);
|
||||||
mv.visitInsn(DUP);
|
} else if (postIncrement.type().equals(TargetType.Double)) {
|
||||||
mv.visitLdcInsn(1F);
|
mv.visitInsn(DUP2);
|
||||||
mv.visitInsn(FSUB);
|
mv.visitLdcInsn(1D);
|
||||||
} else if (postDecrement.type().equals(TargetType.Double)) {
|
mv.visitInsn(DADD);
|
||||||
mv.visitInsn(DUP2);
|
} else if (postIncrement.type().equals(TargetType.Long)) {
|
||||||
mv.visitLdcInsn(1D);
|
mv.visitInsn(DUP2);
|
||||||
mv.visitInsn(DSUB);
|
mv.visitLdcInsn(1L);
|
||||||
} else if (postDecrement.type().equals(TargetType.Long)) {
|
mv.visitInsn(LADD);
|
||||||
mv.visitInsn(DUP2);
|
} else {
|
||||||
mv.visitLdcInsn(1L);
|
mv.visitInsn(DUP);
|
||||||
mv.visitInsn(LSUB);
|
mv.visitLdcInsn(1);
|
||||||
} else {
|
mv.visitInsn(IADD);
|
||||||
mv.visitInsn(DUP);
|
}
|
||||||
mv.visitLdcInsn(1);
|
boxPrimitive(state, postIncrement.type());
|
||||||
mv.visitInsn(ISUB);
|
afterIncDec(state, postIncrement);
|
||||||
|
}
|
||||||
|
case TargetUnaryOp.PostDecrement postDecrement -> {
|
||||||
|
generate(state, postDecrement.expr());
|
||||||
|
if (postDecrement.type().equals(TargetType.Float)) {
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
mv.visitLdcInsn(1F);
|
||||||
|
mv.visitInsn(FSUB);
|
||||||
|
} else if (postDecrement.type().equals(TargetType.Double)) {
|
||||||
|
mv.visitInsn(DUP2);
|
||||||
|
mv.visitLdcInsn(1D);
|
||||||
|
mv.visitInsn(DSUB);
|
||||||
|
} else if (postDecrement.type().equals(TargetType.Long)) {
|
||||||
|
mv.visitInsn(DUP2);
|
||||||
|
mv.visitLdcInsn(1L);
|
||||||
|
mv.visitInsn(LSUB);
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
mv.visitLdcInsn(1);
|
||||||
|
mv.visitInsn(ISUB);
|
||||||
|
}
|
||||||
|
boxPrimitive(state, postDecrement.type());
|
||||||
|
afterIncDec(state, postDecrement);
|
||||||
}
|
}
|
||||||
boxPrimitive(state, postDecrement.type());
|
|
||||||
afterIncDec(state, postDecrement);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,31 +781,19 @@ public class Codegen {
|
|||||||
break;
|
break;
|
||||||
case TargetLiteral literal:
|
case TargetLiteral literal:
|
||||||
switch (literal) {
|
switch (literal) {
|
||||||
case IntLiteral intLiteral:
|
case IntLiteral intLiteral -> mv.visitLdcInsn(intLiteral.value());
|
||||||
mv.visitLdcInsn(intLiteral.value());
|
case FloatLiteral floatLiteral -> mv.visitLdcInsn(floatLiteral.value());
|
||||||
break;
|
case LongLiteral longLiteral -> mv.visitLdcInsn(longLiteral.value());
|
||||||
case FloatLiteral floatLiteral:
|
case StringLiteral stringLiteral -> mv.visitLdcInsn(stringLiteral.value());
|
||||||
mv.visitLdcInsn(floatLiteral.value());
|
case CharLiteral charLiteral -> mv.visitIntInsn(BIPUSH, charLiteral.value());
|
||||||
break;
|
case DoubleLiteral doubleLiteral -> mv.visitLdcInsn(doubleLiteral.value());
|
||||||
case LongLiteral longLiteral:
|
case BooleanLiteral booleanLiteral -> {
|
||||||
mv.visitLdcInsn(longLiteral.value());
|
if (booleanLiteral.value()) {
|
||||||
break;
|
mv.visitInsn(ICONST_1);
|
||||||
case StringLiteral stringLiteral:
|
} else {
|
||||||
mv.visitLdcInsn(stringLiteral.value());
|
mv.visitInsn(ICONST_0);
|
||||||
break;
|
}
|
||||||
case CharLiteral charLiteral:
|
|
||||||
mv.visitIntInsn(BIPUSH, charLiteral.value());
|
|
||||||
break;
|
|
||||||
case DoubleLiteral doubleLiteral:
|
|
||||||
mv.visitLdcInsn(doubleLiteral.value());
|
|
||||||
break;
|
|
||||||
case BooleanLiteral booleanLiteral:
|
|
||||||
if (booleanLiteral.value()) {
|
|
||||||
mv.visitInsn(ICONST_1);
|
|
||||||
} else {
|
|
||||||
mv.visitInsn(ICONST_0);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TargetVarDecl varDecl: {
|
case TargetVarDecl varDecl: {
|
||||||
@ -820,30 +816,27 @@ public class Codegen {
|
|||||||
break;
|
break;
|
||||||
case TargetAssign assign: {
|
case TargetAssign assign: {
|
||||||
switch (assign.left()) {
|
switch (assign.left()) {
|
||||||
case TargetLocalVar localVar: {
|
case TargetLocalVar localVar -> {
|
||||||
generate(state, assign.right());
|
generate(state, assign.right());
|
||||||
convertTo(state, assign.right().type(), localVar.type());
|
convertTo(state, assign.right().type(), localVar.type());
|
||||||
boxPrimitive(state, localVar.type());
|
boxPrimitive(state, localVar.type());
|
||||||
var local = state.scope.get(localVar.name());
|
var local = state.scope.get(localVar.name());
|
||||||
mv.visitInsn(DUP);
|
|
||||||
mv.visitVarInsn(ASTORE, local.index());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TargetFieldVar dot: {
|
|
||||||
var fieldType = dot.type();
|
|
||||||
generate(state, dot.left());
|
|
||||||
generate(state, assign.right());
|
|
||||||
convertTo(state, assign.right().type(), fieldType);
|
|
||||||
boxPrimitive(state, fieldType);
|
|
||||||
if (dot.isStatic())
|
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
else
|
mv.visitVarInsn(ASTORE, local.index());
|
||||||
mv.visitInsn(DUP_X1);
|
}
|
||||||
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.owner().getInternalName(), dot.right(), fieldType.toSignature());
|
case TargetFieldVar dot -> {
|
||||||
break;
|
var fieldType = dot.type();
|
||||||
}
|
generate(state, dot.left());
|
||||||
default:
|
generate(state, assign.right());
|
||||||
throw new CodeGenException("Invalid assignment");
|
convertTo(state, assign.right().type(), fieldType);
|
||||||
|
boxPrimitive(state, fieldType);
|
||||||
|
if (dot.isStatic())
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
else
|
||||||
|
mv.visitInsn(DUP_X1);
|
||||||
|
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.owner().getInternalName(), dot.right(), fieldType.toSignature());
|
||||||
|
}
|
||||||
|
default -> throw new CodeGenException("Invalid assignment");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -890,7 +883,6 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
mv.visitJumpInsn(GOTO, start);
|
mv.visitJumpInsn(GOTO, start);
|
||||||
mv.visitLabel(end);
|
mv.visitLabel(end);
|
||||||
mv.visitInsn(NOP);
|
|
||||||
state.exitScope();
|
state.exitScope();
|
||||||
state.localCounter = localCounter;
|
state.localCounter = localCounter;
|
||||||
break;
|
break;
|
||||||
@ -912,7 +904,6 @@ public class Codegen {
|
|||||||
|
|
||||||
mv.visitJumpInsn(GOTO, start);
|
mv.visitJumpInsn(GOTO, start);
|
||||||
mv.visitLabel(end);
|
mv.visitLabel(end);
|
||||||
mv.visitInsn(NOP);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetIf _if: {
|
case TargetIf _if: {
|
||||||
@ -927,7 +918,6 @@ public class Codegen {
|
|||||||
generate(state, _if.else_body());
|
generate(state, _if.else_body());
|
||||||
}
|
}
|
||||||
mv.visitLabel(end);
|
mv.visitLabel(end);
|
||||||
mv.visitInsn(NOP);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetReturn ret: {
|
case TargetReturn ret: {
|
||||||
@ -940,6 +930,16 @@ public class Codegen {
|
|||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TargetYield yield: {
|
||||||
|
generate(state, yield.expression());
|
||||||
|
try {
|
||||||
|
yieldValue(state, yield.expression().type());
|
||||||
|
mv.visitJumpInsn(GOTO, state.breakStack.peek().endLabel);
|
||||||
|
} catch (EmptyStackException e) {
|
||||||
|
throw new CodeGenException("Yield outside of switch expression");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TargetSwitch _switch: {
|
case TargetSwitch _switch: {
|
||||||
generateSwitch(state, _switch);
|
generateSwitch(state, _switch);
|
||||||
break;
|
break;
|
||||||
@ -1004,13 +1004,25 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void yieldValue(State state, TargetType type) {
|
||||||
|
boxPrimitive(state, type);
|
||||||
|
state.mv.visitVarInsn(ASTORE, state.switchResultValue.peek());
|
||||||
|
}
|
||||||
|
|
||||||
private void generateClassicSwitch(State state, TargetSwitch aSwitch) {
|
private void generateClassicSwitch(State state, TargetSwitch aSwitch) {
|
||||||
// TODO Constant expressions are allowed, we need to evaluate them somehow...
|
// TODO Constant expressions are allowed, we need to evaluate them somehow...
|
||||||
// For now we just assume we get literals
|
// For now we just assume we get literals...
|
||||||
// TODO This always uses a lookupswitch, a tableswitch may be faster in some cases but we can't generate that in all cases
|
// TODO This always uses a lookupswitch, a tableswitch may be faster in some cases but we can't generate that every time
|
||||||
|
// TODO We can't switch on Strings yet, the idea for this (like javac does it) would be to implement the hash code at compile time
|
||||||
|
// and switch based on that, adding an equals check for every case and going to yet another tableswitch which finally decides which branch to take
|
||||||
var mv = state.mv;
|
var mv = state.mv;
|
||||||
|
if (aSwitch.isExpression())
|
||||||
|
state.pushSwitch();
|
||||||
|
|
||||||
generate(state, aSwitch.expr());
|
generate(state, aSwitch.expr());
|
||||||
|
|
||||||
|
state.enterScope();
|
||||||
|
|
||||||
var keys = new int[aSwitch.cases().stream().mapToInt(c -> c.labels().size()).sum()];
|
var keys = new int[aSwitch.cases().stream().mapToInt(c -> c.labels().size()).sum()];
|
||||||
var labels = new Label[keys.length];
|
var labels = new Label[keys.length];
|
||||||
var bodyLabels = new Label[aSwitch.cases().size()];
|
var bodyLabels = new Label[aSwitch.cases().size()];
|
||||||
@ -1043,28 +1055,159 @@ public class Codegen {
|
|||||||
|
|
||||||
for (var k = 0; k < aSwitch.cases().size(); k++) {
|
for (var k = 0; k < aSwitch.cases().size(); k++) {
|
||||||
mv.visitLabel(bodyLabels[k]);
|
mv.visitLabel(bodyLabels[k]);
|
||||||
generate(state, aSwitch.cases().get(k).body());
|
var cse = aSwitch.cases().get(k);
|
||||||
|
generate(state, cse.body());
|
||||||
|
if (cse.isSingleExpression() && aSwitch.isExpression())
|
||||||
|
yieldValue(state, cse.body().statements().get(0).type());
|
||||||
|
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aSwitch.default_() != null) {
|
if (aSwitch.default_() != null) {
|
||||||
mv.visitLabel(defaultLabel);
|
mv.visitLabel(defaultLabel);
|
||||||
generate(state, aSwitch.default_());
|
generate(state, aSwitch.default_().body());
|
||||||
|
if (aSwitch.default_().isSingleExpression() && aSwitch.isExpression())
|
||||||
|
yieldValue(state, aSwitch.default_().body().statements().get(0).type());
|
||||||
}
|
}
|
||||||
|
|
||||||
mv.visitLabel(end);
|
mv.visitLabel(end);
|
||||||
mv.visitInsn(NOP);
|
|
||||||
state.breakStack.pop();
|
state.breakStack.pop();
|
||||||
|
|
||||||
|
if (aSwitch.isExpression()) {
|
||||||
|
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
|
||||||
|
unboxPrimitive(state, aSwitch.type());
|
||||||
|
state.popSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.exitScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateEnhancedSwitch(State state, TargetSwitch aSwitch) {
|
private void generateEnhancedSwitch(State state, TargetSwitch aSwitch) {
|
||||||
|
var mv = state.mv;
|
||||||
|
generate(state, aSwitch.expr());
|
||||||
|
var tmp = state.localCounter++;
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
mv.visitVarInsn(ASTORE, tmp);
|
||||||
|
|
||||||
|
state.enterScope();
|
||||||
|
// This is the index to start the switch from
|
||||||
|
mv.visitInsn(ICONST_0);
|
||||||
|
if (aSwitch.isExpression())
|
||||||
|
state.pushSwitch();
|
||||||
|
|
||||||
|
// To be able to skip ahead to the next case
|
||||||
|
var start = new Label();
|
||||||
|
mv.visitLabel(start);
|
||||||
|
|
||||||
|
var end = new Label();
|
||||||
|
var env = new BreakEnv();
|
||||||
|
env.endLabel = end;
|
||||||
|
state.breakStack.push(env);
|
||||||
|
|
||||||
|
var mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);
|
||||||
|
var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/runtime/SwitchBootstraps", "typeSwitch", mt.toMethodDescriptorString(), false);
|
||||||
|
|
||||||
|
var types = new Object[aSwitch.cases().size()];
|
||||||
|
for (var i = 0; i < types.length; i++) {
|
||||||
|
var cse = aSwitch.cases().get(i);
|
||||||
|
var label = cse.labels().get(0);
|
||||||
|
if (label instanceof TargetSwitch.SimplePattern || label instanceof TargetSwitch.ComplexPattern)
|
||||||
|
types[i] = Type.getObjectType(label.type().getInternalName());
|
||||||
|
else if (label instanceof TargetLiteral lit)
|
||||||
|
types[i] = lit.value();
|
||||||
|
else if (label instanceof TargetSwitch.Guard guard)
|
||||||
|
types[i] = Type.getObjectType(guard.inner().type().getInternalName());
|
||||||
|
// TODO Same here we need to evaluate constants
|
||||||
|
else throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
mv.visitInvokeDynamicInsn("typeSwitch", "(Ljava/lang/Object;I)I", bootstrap, types);
|
||||||
|
|
||||||
|
var caseLabels = new Label[aSwitch.cases().size()];
|
||||||
|
var labels = new Label[aSwitch.cases().stream().mapToInt(c -> c.labels().size()).sum()];
|
||||||
|
var j = 0;
|
||||||
|
for (var i = 0; i < caseLabels.length; i++) {
|
||||||
|
var cse = aSwitch.cases().get(i);
|
||||||
|
var label = new Label();
|
||||||
|
caseLabels[i] = label;
|
||||||
|
for (var k = 0; k < cse.labels().size(); k++) {
|
||||||
|
labels[j] = label;
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultLabel = end;
|
||||||
|
if (aSwitch.default_() != null) {
|
||||||
|
defaultLabel = new Label();
|
||||||
|
}
|
||||||
|
|
||||||
|
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
|
||||||
|
|
||||||
|
for (var i = 0; i < aSwitch.cases().size(); i++) {
|
||||||
|
mv.visitLabel(caseLabels[i]);
|
||||||
|
var cse = aSwitch.cases().get(i);
|
||||||
|
|
||||||
|
if (cse.labels().size() == 1) {
|
||||||
|
var label = cse.labels().get(0);
|
||||||
|
if (label instanceof TargetSwitch.Guard gd)
|
||||||
|
bindLabel(state, tmp, aSwitch.expr().type(), gd.inner());
|
||||||
|
else if (label instanceof TargetSwitch.Pattern pat)
|
||||||
|
bindLabel(state, tmp, aSwitch.expr().type(), pat);
|
||||||
|
|
||||||
|
if (label instanceof TargetSwitch.Guard gd) {
|
||||||
|
generate(state, gd.expression());
|
||||||
|
var next = new Label();
|
||||||
|
mv.visitJumpInsn(IFNE, next);
|
||||||
|
mv.visitVarInsn(ALOAD, tmp);
|
||||||
|
// Push the offset onto the stack (this is used by the invokedynamic call)
|
||||||
|
mv.visitLdcInsn(i + 1);
|
||||||
|
mv.visitJumpInsn(GOTO, start);
|
||||||
|
mv.visitLabel(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate(state, cse.body());
|
||||||
|
if (cse.isSingleExpression() && aSwitch.isExpression())
|
||||||
|
yieldValue(state, cse.body().statements().get(0).type());
|
||||||
|
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aSwitch.default_() != null) {
|
||||||
|
mv.visitLabel(defaultLabel);
|
||||||
|
generate(state, aSwitch.default_().body());
|
||||||
|
if (aSwitch.default_().isSingleExpression() && aSwitch.isExpression())
|
||||||
|
yieldValue(state, aSwitch.default_().body().statements().get(0).type());
|
||||||
|
}
|
||||||
|
|
||||||
|
mv.visitLabel(end);
|
||||||
|
//mv.visitInsn(POP);
|
||||||
|
|
||||||
|
state.breakStack.pop();
|
||||||
|
if (aSwitch.isExpression()) {
|
||||||
|
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
|
||||||
|
unboxPrimitive(state, aSwitch.type());
|
||||||
|
state.popSwitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.exitScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindLabel(State state, int tmp, TargetType type, TargetSwitch.Pattern pat) {
|
||||||
|
if (pat instanceof TargetSwitch.SimplePattern sp) {
|
||||||
|
state.mv.visitVarInsn(ALOAD, tmp);
|
||||||
|
var local = state.createVariable(sp.name(), sp.type());
|
||||||
|
convertTo(state, type, local.type);
|
||||||
|
boxPrimitive(state, local.type);
|
||||||
|
state.mv.visitVarInsn(ASTORE, local.index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<TargetType> wrapperTypes = Set.of(TargetType.Long, TargetType.Integer, TargetType.Byte, TargetType.Char, TargetType.Boolean, TargetType.Double, TargetType.Float);
|
final Set<TargetType> wrapperTypes = Set.of(TargetType.Long, TargetType.Integer, TargetType.Byte, TargetType.Char, TargetType.Boolean, TargetType.Double, TargetType.Float);
|
||||||
|
|
||||||
private void generateSwitch(State state, TargetSwitch aSwitch) {
|
private void generateSwitch(State state, TargetSwitch aSwitch) {
|
||||||
if (!wrapperTypes.contains(aSwitch.expr().type()))
|
if (!wrapperTypes.contains(aSwitch.expr().type())) {
|
||||||
generateEnhancedSwitch(state, aSwitch);
|
generateEnhancedSwitch(state, aSwitch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
else for (var case_ : aSwitch.cases()) {
|
else for (var case_ : aSwitch.cases()) {
|
||||||
if (case_.labels().stream().anyMatch(c -> c instanceof TargetSwitch.Pattern)) {
|
if (case_.labels().stream().anyMatch(c -> c instanceof TargetSwitch.Pattern)) {
|
||||||
generateEnhancedSwitch(state, aSwitch);
|
generateEnhancedSwitch(state, aSwitch);
|
||||||
|
@ -20,7 +20,6 @@ import de.dhbwstuttgart.parser.antlr.Java17Parser.AssignexpressionContext;
|
|||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseandexpressionContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseandexpressionContext;
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseorexpressionContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseorexpressionContext;
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwisexorexpressionContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwisexorexpressionContext;
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockStatementContext;
|
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockstmtContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockstmtContext;
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BoolLiteralContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BoolLiteralContext;
|
||||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BreakstmtContext;
|
import de.dhbwstuttgart.parser.antlr.Java17Parser.BreakstmtContext;
|
||||||
@ -381,7 +380,7 @@ public class StatementGenerator {
|
|||||||
return new Switch(switched, switchBlocks, TypePlaceholder.fresh(offset), false, offset);
|
return new Switch(switched, switchBlocks, TypePlaceholder.fresh(offset), false, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwitchBlock convert(Java17Parser.SwitchLabeledRuleContext labeledRule) {
|
private SwitchBlock convert(SwitchLabeledRuleContext labeledRule) {
|
||||||
Boolean isDefault = false;
|
Boolean isDefault = false;
|
||||||
List<SwitchLabel> labels = switch (labeledRule.switchLabelCase()) {
|
List<SwitchLabel> labels = switch (labeledRule.switchLabelCase()) {
|
||||||
case LabeledRuleExprListContext exprList -> {
|
case LabeledRuleExprListContext exprList -> {
|
||||||
@ -401,11 +400,14 @@ public class StatementGenerator {
|
|||||||
}
|
}
|
||||||
default -> throw new NotImplementedException();
|
default -> throw new NotImplementedException();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isSingleExpression = false;
|
||||||
Token offset = labeledRule.getStart();
|
Token offset = labeledRule.getStart();
|
||||||
SwitchRuleOutcomeContext outcome = labeledRule.switchRuleOutcome();
|
SwitchRuleOutcomeContext outcome = labeledRule.switchRuleOutcome();
|
||||||
Block block;
|
Block block;
|
||||||
if (Objects.isNull(outcome.block())) {
|
if (Objects.isNull(outcome.block())) {
|
||||||
List<Statement> stmts = new ArrayList<>();
|
List<Statement> stmts = new ArrayList<>();
|
||||||
|
if (outcome.blockStatement().size() == 1) isSingleExpression = true;
|
||||||
outcome.blockStatement().stream().forEach((stmt) -> {
|
outcome.blockStatement().stream().forEach((stmt) -> {
|
||||||
stmts.addAll(convert(stmt));
|
stmts.addAll(convert(stmt));
|
||||||
});
|
});
|
||||||
@ -414,7 +416,7 @@ public class StatementGenerator {
|
|||||||
} else {
|
} else {
|
||||||
block = convert(outcome.block(), false);
|
block = convert(outcome.block(), false);
|
||||||
}
|
}
|
||||||
return new SwitchBlock(labels, block, isDefault, offset);
|
return new SwitchBlock(labels, block, isDefault, isSingleExpression, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Statement convert(Java17Parser.YieldstmtContext yieldstmt) {
|
private Statement convert(Java17Parser.YieldstmtContext yieldstmt) {
|
||||||
@ -430,7 +432,7 @@ public class StatementGenerator {
|
|||||||
stmt.blockStatement().stream().forEach((blockStmt) -> {
|
stmt.blockStatement().stream().forEach((blockStmt) -> {
|
||||||
block.addAll(convert(blockStmt));
|
block.addAll(convert(blockStmt));
|
||||||
});
|
});
|
||||||
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), stmt.getStart());
|
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), false, stmt.getStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwitchLabel convert(SwitchLabelContext switchLabel) {
|
private SwitchLabel convert(SwitchLabelContext switchLabel) {
|
||||||
@ -454,17 +456,15 @@ public class StatementGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pattern convert(PatternContext pattern) {
|
private Expression convert(PatternContext pattern) {
|
||||||
return switch (pattern) {
|
return switch (pattern) {
|
||||||
case PPatternContext pPattern -> {
|
case PPatternContext pPattern -> {
|
||||||
yield convert(pPattern.primaryPattern());
|
yield convert(pPattern.primaryPattern());
|
||||||
}
|
}
|
||||||
case GPatternContext gPattern -> {
|
case GPatternContext gPattern -> {
|
||||||
GuardedPatternContext guarded = gPattern.guardedPattern();
|
GuardedPatternContext guarded = gPattern.guardedPattern();
|
||||||
List<Expression> conditions = guarded.expression().stream().map((expr) -> {
|
Expression condition = convert(guarded.expression());
|
||||||
return convert(expr);
|
yield new GuardedPattern(condition, convert(guarded.primaryPattern()), guarded.getStart());
|
||||||
}).toList();
|
|
||||||
yield new GuardedPattern(conditions, guarded.identifier().getText(), TypeGenerator.convert(guarded.typeType(), reg, generics), guarded.getStart());
|
|
||||||
}
|
}
|
||||||
default -> throw new NotImplementedException();
|
default -> throw new NotImplementedException();
|
||||||
};
|
};
|
||||||
@ -487,7 +487,7 @@ public class StatementGenerator {
|
|||||||
private RecordPattern convert(RecordPatternContext recordPatternCtx) {
|
private RecordPattern convert(RecordPatternContext recordPatternCtx) {
|
||||||
List<PatternContext> subPatternCtx = recordPatternCtx.recordStructurePattern().recordComponentPatternList().pattern();
|
List<PatternContext> subPatternCtx = recordPatternCtx.recordStructurePattern().recordComponentPatternList().pattern();
|
||||||
List<Pattern> subPattern = subPatternCtx.stream().map((patternCtx) -> {
|
List<Pattern> subPattern = subPatternCtx.stream().map((patternCtx) -> {
|
||||||
return 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());
|
return new RecordPattern(subPattern, (identifierCtx != null) ? identifierCtx.getText() : null, TypeGenerator.convert(recordPatternCtx.typeType(), reg, generics), recordPatternCtx.getStart());
|
||||||
|
@ -2,21 +2,32 @@ package de.dhbwstuttgart.syntaxtree.statement;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
||||||
import org.antlr.v4.runtime.Token;
|
import org.antlr.v4.runtime.Token;
|
||||||
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
||||||
|
|
||||||
public class GuardedPattern extends Pattern {
|
public class GuardedPattern extends Expression {
|
||||||
|
|
||||||
private List<Expression> conditions;
|
private final Expression condition;
|
||||||
|
private final Pattern nested;
|
||||||
|
|
||||||
public GuardedPattern(List<Expression> conditions, String name, RefTypeOrTPHOrWildcardOrGeneric type, Token offset) {
|
public GuardedPattern(Expression condition, Pattern nested, Token offset) {
|
||||||
super(name, type, offset);
|
super(nested.getType(), offset);
|
||||||
this.conditions = conditions;
|
this.condition = condition;
|
||||||
|
this.nested = nested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Expression> getConditions() {
|
public Expression getCondition() {
|
||||||
return conditions;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Pattern getNestedPattern() {
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(StatementVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,19 @@ public class SwitchBlock extends Block {
|
|||||||
private List<SwitchLabel> labels = new ArrayList<>();
|
private List<SwitchLabel> labels = new ArrayList<>();
|
||||||
|
|
||||||
private boolean defaultBlock = false;
|
private boolean defaultBlock = false;
|
||||||
|
public final boolean isExpression; // This is for single expressions that yield a value
|
||||||
|
|
||||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, Token offset) {
|
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isExpression, Token offset) {
|
||||||
super(statements.getStatements(), offset);
|
super(statements.getStatements(), offset);
|
||||||
this.labels = labels;
|
this.labels = labels;
|
||||||
|
this.isExpression = isExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isDefault, Token offset) {
|
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isDefault, boolean isExpression, Token offset) {
|
||||||
super(statements.getStatements(), offset);
|
super(statements.getStatements(), offset);
|
||||||
this.labels = labels;
|
this.labels = labels;
|
||||||
this.defaultBlock = isDefault;
|
this.defaultBlock = isDefault;
|
||||||
|
this.isExpression = isExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDefault() {
|
public boolean isDefault() {
|
||||||
|
@ -474,11 +474,8 @@ public class OutputGenerator implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GuardedPattern aGuardedPattern) {
|
public void visit(GuardedPattern aGuardedPattern) {
|
||||||
aGuardedPattern.getType().accept(this);
|
aGuardedPattern.getNestedPattern().accept(this);
|
||||||
out.append(aGuardedPattern.getName());
|
out.append(" with ");
|
||||||
for (Expression cond : aGuardedPattern.getConditions()) {
|
aGuardedPattern.getCondition().accept(this);
|
||||||
out.append("&&");
|
|
||||||
cond.accept(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -208,7 +208,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected TargetSwitch.Case convert(SwitchBlock block) {
|
protected TargetSwitch.Case convert(SwitchBlock block) {
|
||||||
return new TargetSwitch.Case(block.getLabels().stream().map(this::convert).toList(), convert((Block) block));
|
return new TargetSwitch.Case(block.getLabels().stream().map(this::convert).toList(), convert((Block) block), block.isExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TargetBlock convert(Block block) {
|
protected TargetBlock convert(Block block) {
|
||||||
|
@ -344,8 +344,14 @@ public class StatementToTargetExpression implements StatementVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visit(Switch switchStmt) {
|
public void visit(Switch switchStmt) {
|
||||||
var cases = switchStmt.getBlocks().stream().filter(s -> !s.isDefault()).map(converter::convert).toList();
|
var cases = switchStmt.getBlocks().stream().filter(s -> !s.isDefault()).map(converter::convert).toList();
|
||||||
var default_ = switchStmt.getBlocks().stream().filter(SwitchBlock::isDefault).map(s -> converter.convert((Block) s)).findFirst().orElse(null);
|
|
||||||
result = new TargetSwitch(converter.convert(switchStmt.getSwitch()), cases, default_, converter.convert(switchStmt.getType()), !switchStmt.getStatement());
|
TargetSwitch.Case default_ = null;
|
||||||
|
for (var block : switchStmt.getBlocks()) {
|
||||||
|
if (block.isDefault()) {
|
||||||
|
default_ = new TargetSwitch.Case(converter.convert((Block) block), block.isExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = new TargetSwitch(converter.convert(switchStmt.getSwitch()), cases, default_ , converter.convert(switchStmt.getType()), !switchStmt.getStatement());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -374,8 +380,6 @@ public class StatementToTargetExpression implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GuardedPattern aGuardedPattern) {
|
public void visit(GuardedPattern aGuardedPattern) {
|
||||||
//FIXME This isn't done properly inside the parser, really you should only have one guard (Chaining them together with && just yields another expression)
|
result = new TargetSwitch.Guard((TargetSwitch.Pattern) converter.convert(aGuardedPattern.getNestedPattern()), converter.convert(aGuardedPattern.getCondition()));
|
||||||
//And then it also needs to be able to accept complex patterns. Because of this we only accept one condition for now.
|
|
||||||
result = new TargetSwitch.Guard(new TargetSwitch.SimplePattern(converter.convert(aGuardedPattern.getType()), aGuardedPattern.getName()), converter.convert(aGuardedPattern.getConditions().get(0)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,33 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type, boolean isExpression) implements TargetExpression {
|
public record TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, TargetType type, boolean isExpression) implements TargetExpression {
|
||||||
|
|
||||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_) {
|
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_) {
|
||||||
this(expr, cases, default_, null, false);
|
this(expr, cases, default_, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type) {
|
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, TargetType type) {
|
||||||
this(expr, cases, default_, type, true);
|
this(expr, cases, default_, type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, boolean isExpression) {
|
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, boolean isExpression) {
|
||||||
this(expr, cases, default_, null, isExpression);
|
this(expr, cases, default_, null, isExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Case(List<TargetExpression> labels, TargetBlock body) {}
|
public record Case(List<TargetExpression> labels, TargetBlock body, boolean isSingleExpression) {
|
||||||
|
public Case(List<TargetExpression> labels, TargetBlock body) {
|
||||||
|
this(labels, body, false);
|
||||||
|
}
|
||||||
|
public Case(TargetBlock body, boolean isSingleExpression) {
|
||||||
|
this(List.of(), body, isSingleExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public sealed interface Pattern extends TargetExpression {}
|
public sealed interface Pattern extends TargetExpression {}
|
||||||
|
|
||||||
public record SimplePattern(TargetType type, String name) implements Pattern {}
|
public record SimplePattern(TargetType type, String name) implements Pattern {}
|
||||||
public record ComplexPattern(TargetType type, List<Pattern> subPatterns) implements Pattern {}
|
public record ComplexPattern(TargetType type, List<Pattern> subPatterns) implements Pattern {}
|
||||||
|
|
||||||
public record Guard(TargetExpression inner, TargetExpression expression) implements Pattern {}
|
public record Guard(Pattern inner, TargetExpression expression) implements Pattern {}
|
||||||
}
|
}
|
||||||
|
@ -200,46 +200,58 @@ public class TestCodegen {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClassicSwitch() throws Exception {
|
public void testClassicSwitch() throws Exception {
|
||||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "Switch");
|
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "SwitchClassic");
|
||||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchClassic", List.of(new MethodParameter(TargetType.Integer, "i")), TargetType.Integer, new TargetBlock(List.of(
|
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 TargetVarDecl(TargetType.Integer, "res", null),
|
||||||
new TargetSwitch(new TargetLocalVar(TargetType.Integer, "i"), List.of(
|
new TargetSwitch(new TargetLocalVar(TargetType.Integer, "i"), List.of(
|
||||||
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(10)), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(10)), new TargetBlock(
|
||||||
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(0)), new TargetBreak())
|
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(0)), new TargetBreak())
|
||||||
)),
|
)),
|
||||||
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(20)), new TargetBlock(List.of())),
|
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(15), new TargetLiteral.IntLiteral(20)), new TargetBlock(List.of())),
|
||||||
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(30)), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(30)), new TargetBlock(
|
||||||
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(1)), new TargetBreak())
|
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(1)), new TargetBreak())
|
||||||
))
|
))
|
||||||
), new TargetBlock(
|
), new TargetSwitch.Case(new TargetBlock(
|
||||||
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(2)), new TargetBreak())
|
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(2)), new TargetBreak())
|
||||||
)),
|
), false)),
|
||||||
new TargetReturn(new TargetLocalVar(TargetType.Integer, "res"))
|
new TargetReturn(new TargetLocalVar(TargetType.Integer, "res"))
|
||||||
)));
|
)));
|
||||||
|
|
||||||
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
|
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
|
||||||
|
var m = clazz.getDeclaredMethod("switchClassic", Integer.class);
|
||||||
|
assertEquals(m.invoke(null, 10), 0);
|
||||||
|
assertEquals(m.invoke(null, 15), 1);
|
||||||
|
assertEquals(m.invoke(null, 20), 1);
|
||||||
|
assertEquals(m.invoke(null, 30), 1);
|
||||||
|
assertEquals(m.invoke(null, 99), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTypeSwitch() throws Exception {
|
public void testTypeSwitch() throws Exception {
|
||||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Switch");
|
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "SwitchEnhanced");
|
||||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchType", List.of(new MethodParameter(TargetType.Object, "obj")), TargetType.Integer, new TargetBlock(List.of(
|
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 TargetReturn(new TargetSwitch(new TargetLocalVar(TargetType.Object, "obj"), List.of(
|
||||||
new TargetSwitch.Case(List.of(new TargetSwitch.SimplePattern(TargetType.String, "aString")), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetSwitch.SimplePattern(TargetType.String, "aString")), new TargetBlock(
|
||||||
List.of(new TargetLiteral.IntLiteral(0))
|
List.of(new TargetYield(new TargetLiteral.IntLiteral(0)))
|
||||||
)),
|
), false),
|
||||||
|
new TargetSwitch.Case(List.of(
|
||||||
|
new TargetSwitch.Guard(new TargetSwitch.SimplePattern(TargetType.Integer, "i"), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)))
|
||||||
|
), new TargetBlock(
|
||||||
|
List.of(new TargetLiteral.IntLiteral(3))
|
||||||
|
), true),
|
||||||
new TargetSwitch.Case(List.of(new TargetSwitch.SimplePattern(TargetType.Integer, "anInteger")), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetSwitch.SimplePattern(TargetType.Integer, "anInteger")), new TargetBlock(
|
||||||
List.of(new TargetLiteral.IntLiteral(1))
|
List.of(new TargetLiteral.IntLiteral(1))
|
||||||
))
|
), true)
|
||||||
), new TargetBlock(
|
), new TargetSwitch.Case(new TargetBlock(
|
||||||
List.of(new TargetLiteral.IntLiteral(2))
|
List.of(new TargetLiteral.IntLiteral(2))
|
||||||
), TargetType.Integer)
|
), true), TargetType.Integer)
|
||||||
))));
|
))));
|
||||||
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
|
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
|
||||||
var m = clazz.getDeclaredMethod("switchType", Object.class);
|
var m = clazz.getDeclaredMethod("switchType", Object.class);
|
||||||
assertEquals(m.invoke(null, "String"), 0);
|
assertEquals(m.invoke(null, "String"), 0);
|
||||||
assertEquals(m.invoke(null, 10), 1);
|
assertEquals(m.invoke(null, 10), 1);
|
||||||
assertEquals(m.invoke(null, 'A'), 2);
|
assertEquals(m.invoke(null, 'A'), 2);
|
||||||
|
assertEquals(m.invoke(null, 5), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user