Add test for instanceof
This commit is contained in:
parent
bffc7f9f1c
commit
729e88bc6d
18
resources/bytecode/javFiles/InstanceOf.jav
Normal file
18
resources/bytecode/javFiles/InstanceOf.jav
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import java.lang.Number;
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Double;
|
||||||
|
import java.lang.String;
|
||||||
|
|
||||||
|
public class InstanceOf {
|
||||||
|
main(n) {
|
||||||
|
if (n instanceof Integer i) {
|
||||||
|
takes(i);
|
||||||
|
return "Integer";
|
||||||
|
} else if (n instanceof Double d) {
|
||||||
|
takes(d);
|
||||||
|
return "Double";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
takes(i) {} // Should be overloaded
|
||||||
|
}
|
@ -782,7 +782,7 @@ public class Codegen {
|
|||||||
convertTo(state, cast.expr().type(), cast.type());
|
convertTo(state, cast.expr().type(), cast.type());
|
||||||
break;
|
break;
|
||||||
case TargetInstanceOf instanceOf:
|
case TargetInstanceOf instanceOf:
|
||||||
mv.visitTypeInsn(INSTANCEOF, instanceOf.right().getInternalName());
|
generateInstanceOf(state, instanceOf);
|
||||||
break;
|
break;
|
||||||
case TargetLiteral literal:
|
case TargetLiteral literal:
|
||||||
switch (literal) {
|
switch (literal) {
|
||||||
@ -1009,6 +1009,17 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateInstanceOf(State state, TargetInstanceOf instanceOf) {
|
||||||
|
var mv = state.mv;
|
||||||
|
|
||||||
|
if (instanceOf.right() instanceof TargetTypePattern right && right.name() == null) {
|
||||||
|
mv.visitTypeInsn(INSTANCEOF, right.type().getInternalName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void yieldValue(State state, TargetType type) {
|
private void yieldValue(State state, TargetType type) {
|
||||||
boxPrimitive(state, type);
|
boxPrimitive(state, type);
|
||||||
state.mv.visitVarInsn(ASTORE, state.switchResultValue.peek());
|
state.mv.visitVarInsn(ASTORE, state.switchResultValue.peek());
|
||||||
@ -1113,11 +1124,11 @@ public class Codegen {
|
|||||||
|
|
||||||
var types = new ArrayList<Object>(aSwitch.cases().size());
|
var types = new ArrayList<Object>(aSwitch.cases().size());
|
||||||
for (var cse : aSwitch.cases()) for (var label : cse.labels()) {
|
for (var cse : aSwitch.cases()) for (var label : cse.labels()) {
|
||||||
if (label instanceof SimplePattern || label instanceof ComplexPattern)
|
if (label instanceof TargetTypePattern || label instanceof TargetComplexPattern)
|
||||||
types.add(Type.getObjectType(label.type().getInternalName()));
|
types.add(Type.getObjectType(label.type().getInternalName()));
|
||||||
else if (label instanceof TargetLiteral lit)
|
else if (label instanceof TargetLiteral lit)
|
||||||
types.add(lit.value());
|
types.add(lit.value());
|
||||||
else if (label instanceof Guard guard)
|
else if (label instanceof TargetGuard guard)
|
||||||
types.add(Type.getObjectType(guard.inner().type().getInternalName()));
|
types.add(Type.getObjectType(guard.inner().type().getInternalName()));
|
||||||
// TODO Same here we need to evaluate constant;
|
// TODO Same here we need to evaluate constant;
|
||||||
else throw new NotImplementedException();
|
else throw new NotImplementedException();
|
||||||
@ -1151,7 +1162,7 @@ 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 TargetGuard gd) {
|
||||||
state.mv.visitVarInsn(ALOAD, tmp);
|
state.mv.visitVarInsn(ALOAD, tmp);
|
||||||
bindPattern(state, aSwitch.expr().type(), gd.inner(), start, i, 1);
|
bindPattern(state, aSwitch.expr().type(), gd.inner(), start, i, 1);
|
||||||
} else if (label instanceof TargetPattern pat) {
|
} else if (label instanceof TargetPattern pat) {
|
||||||
@ -1159,7 +1170,7 @@ public class Codegen {
|
|||||||
bindPattern(state, aSwitch.expr().type(), pat, start, i, 1);
|
bindPattern(state, aSwitch.expr().type(), pat, start, i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (label instanceof Guard gd) {
|
if (label instanceof TargetGuard gd) {
|
||||||
generate(state, gd.expression());
|
generate(state, gd.expression());
|
||||||
var next = new Label();
|
var next = new Label();
|
||||||
mv.visitJumpInsn(IFNE, next);
|
mv.visitJumpInsn(IFNE, next);
|
||||||
@ -1216,10 +1227,10 @@ public class Codegen {
|
|||||||
|
|
||||||
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
|
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
|
||||||
|
|
||||||
if (pat instanceof SimplePattern sp) {
|
if (pat instanceof TargetTypePattern sp) {
|
||||||
var local = state.createVariable(sp.name(), sp.type());
|
var local = state.createVariable(sp.name(), sp.type());
|
||||||
state.mv.visitVarInsn(ASTORE, local.index);
|
state.mv.visitVarInsn(ASTORE, local.index);
|
||||||
} else if (pat instanceof ComplexPattern cp) {
|
} else if (pat instanceof TargetComplexPattern cp) {
|
||||||
if (cp.name() != null) {
|
if (cp.name() != null) {
|
||||||
state.mv.visitInsn(DUP);
|
state.mv.visitInsn(DUP);
|
||||||
var local = state.createVariable(cp.name(), cp.type());
|
var local = state.createVariable(cp.name(), cp.type());
|
||||||
|
@ -875,8 +875,8 @@ public class StatementGenerator {
|
|||||||
private Expression convert(Java17Parser.InstanceofexpressionContext expression) {
|
private Expression convert(Java17Parser.InstanceofexpressionContext expression) {
|
||||||
Expression left = convert(expression.expression());
|
Expression left = convert(expression.expression());
|
||||||
Token offset = expression.getStart();
|
Token offset = expression.getStart();
|
||||||
if (Objects.isNull(expression.pattern())) {
|
if (Objects.isNull(expression.pattern())) {;
|
||||||
return new InstanceOf(left, TypeGenerator.convert(expression.typeType(), reg, generics), offset);
|
return new InstanceOf(left, new RefType(reg.getName("java.lang.Boolean"), expression.getStart()), TypeGenerator.convert(expression.typeType(), reg, generics), offset);
|
||||||
} else {
|
} else {
|
||||||
switch (expression.pattern()) {
|
switch (expression.pattern()) {
|
||||||
case PPatternContext primaryPattern:
|
case PPatternContext primaryPattern:
|
||||||
@ -886,7 +886,7 @@ public class StatementGenerator {
|
|||||||
String localVarName = typePatternCtx.identifier().getText();
|
String localVarName = typePatternCtx.identifier().getText();
|
||||||
RefTypeOrTPHOrWildcardOrGeneric localVarType = TypeGenerator.convert(typePatternCtx.typeType(), reg, generics);
|
RefTypeOrTPHOrWildcardOrGeneric localVarType = TypeGenerator.convert(typePatternCtx.typeType(), reg, generics);
|
||||||
localVars.put(localVarName, localVarType);
|
localVars.put(localVarName, localVarType);
|
||||||
return new InstanceOf(left, new FormalParameter(localVarName, localVarType, typePatternCtx.getStart()), offset);
|
return new InstanceOf(left, new RefType(reg.getName("java.lang.Boolean"), expression.getStart()), new FormalParameter(localVarName, localVarType, typePatternCtx.getStart()), offset);
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,7 @@ public class BinaryExpr extends Expression {
|
|||||||
LESSEQUAL, // <=
|
LESSEQUAL, // <=
|
||||||
BIGGEREQUAL, // >=
|
BIGGEREQUAL, // >=
|
||||||
EQUAL, // ==
|
EQUAL, // ==
|
||||||
NOTEQUAL, // !=
|
NOTEQUAL // !=
|
||||||
INSTOF // instanceof
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Operator operation;
|
public final Operator operation;
|
||||||
|
@ -6,33 +6,31 @@ import org.antlr.v4.runtime.Token;
|
|||||||
|
|
||||||
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
|
||||||
|
|
||||||
public class InstanceOf extends BinaryExpr {
|
public class InstanceOf extends Expression {
|
||||||
private FormalParameter pattern;
|
private final Pattern pattern;
|
||||||
|
private final Expression expr;
|
||||||
|
|
||||||
public InstanceOf(Expression expr, RefTypeOrTPHOrWildcardOrGeneric reftype, Token offset) {
|
public InstanceOf(Expression expr, RefTypeOrTPHOrWildcardOrGeneric type, RefTypeOrTPHOrWildcardOrGeneric reftype, Token offset) {
|
||||||
super(BinaryExpr.Operator.INSTOF, TypePlaceholder.fresh(offset), expr, new LocalVar("", reftype, reftype.getOffset()), offset);
|
super(type, offset);
|
||||||
this.pattern = new FormalParameter(null, reftype, offset);
|
this.pattern = new FormalParameter(null, reftype, offset);
|
||||||
|
this.expr = expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceOf(Expression expr, FormalParameter pattern, Token offset) {
|
public InstanceOf(Expression expr, RefTypeOrTPHOrWildcardOrGeneric type, Pattern pattern, Token offset) {
|
||||||
super(BinaryExpr.Operator.INSTOF, TypePlaceholder.fresh(offset), expr, new LocalVar(pattern.getName(), pattern.getType(), pattern.getOffset()), offset);
|
super(type, offset);
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
|
this.expr = expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RefTypeOrTPHOrWildcardOrGeneric getReftype() {
|
public Pattern getPattern() {
|
||||||
return pattern.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return pattern.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FormalParameter gPattern() {
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Expression getExpression() {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(StatementVisitor visitor) {
|
public void accept(StatementVisitor visitor) {
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
|
@ -383,7 +383,8 @@ public abstract class GenerateGenerics {
|
|||||||
superType = new Void(new NullToken());
|
superType = new Void(new NullToken());
|
||||||
ifStmt.then_block.accept(this);
|
ifStmt.then_block.accept(this);
|
||||||
superType = new Void(new NullToken());
|
superType = new Void(new NullToken());
|
||||||
ifStmt.else_block.accept(this);
|
if (ifStmt.else_block != null)
|
||||||
|
ifStmt.else_block.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -143,12 +143,12 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(IfStmt ifStmt) {
|
public void visit(IfStmt ifStmt) {
|
||||||
result = new TargetIf(converter.convert(ifStmt.expr), converter.convert(ifStmt.then_block), converter.convert(ifStmt.else_block));
|
result = new TargetIf(converter.convert(ifStmt.expr), converter.convert(ifStmt.then_block), ifStmt.else_block != null ? converter.convert(ifStmt.else_block) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InstanceOf instanceOf) {
|
public void visit(InstanceOf instanceOf) {
|
||||||
result = new TargetInstanceOf(converter.convert(instanceOf.lexpr), converter.convert(instanceOf.rexpr.getType()));
|
result = new TargetInstanceOf(converter.convert(instanceOf.getExpression()), converter.convert(instanceOf.getPattern()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -433,7 +433,7 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(FormalParameter aPattern) {
|
public void visit(FormalParameter aPattern) {
|
||||||
result = new SimplePattern(converter.convert(aPattern.getType()), aPattern.getName());
|
result = new TargetTypePattern(converter.convert(aPattern.getType()), aPattern.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -443,7 +443,7 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(RecordPattern aRecordPattern) {
|
public void visit(RecordPattern aRecordPattern) {
|
||||||
result = new ComplexPattern(
|
result = new TargetComplexPattern(
|
||||||
converter.convert(aRecordPattern.getType()),
|
converter.convert(aRecordPattern.getType()),
|
||||||
aRecordPattern.getName(),
|
aRecordPattern.getName(),
|
||||||
aRecordPattern.getSubPattern().stream().map(x -> (TargetPattern) converter.convert(x)).toList()
|
aRecordPattern.getSubPattern().stream().map(x -> (TargetPattern) converter.convert(x)).toList()
|
||||||
@ -452,6 +452,6 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GuardedPattern aGuardedPattern) {
|
public void visit(GuardedPattern aGuardedPattern) {
|
||||||
result = new Guard((TargetPattern) converter.convert(aGuardedPattern.getNestedPattern()), converter.convert(aGuardedPattern.getCondition()));
|
result = new TargetGuard((TargetPattern) converter.convert(aGuardedPattern.getNestedPattern()), converter.convert(aGuardedPattern.getCondition()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,8 @@ public abstract class TracingStatementVisitor implements StatementVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visit(IfStmt ifStmt) {
|
public void visit(IfStmt ifStmt) {
|
||||||
ifStmt.then_block.accept(this);
|
ifStmt.then_block.accept(this);
|
||||||
ifStmt.else_block.accept(this);
|
if (ifStmt.else_block != null)
|
||||||
|
ifStmt.else_block.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
package de.dhbwstuttgart.target.tree.expression;
|
|
||||||
|
|
||||||
public record Guard(TargetPattern inner, TargetExpression expression) implements TargetPattern {
|
|
||||||
}
|
|
@ -4,5 +4,5 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record ComplexPattern(TargetType type, String name, List<TargetPattern> subPatterns) implements TargetPattern {
|
public record TargetComplexPattern(TargetType type, String name, List<TargetPattern> subPatterns) implements TargetPattern {
|
||||||
}
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package de.dhbwstuttgart.target.tree.expression;
|
||||||
|
|
||||||
|
public record TargetGuard(TargetPattern inner, TargetExpression expression) implements TargetPattern {
|
||||||
|
}
|
@ -3,7 +3,7 @@ package de.dhbwstuttgart.target.tree.expression;
|
|||||||
import de.dhbwstuttgart.syntaxtree.statement.Expression;
|
import de.dhbwstuttgart.syntaxtree.statement.Expression;
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
|
|
||||||
public record TargetInstanceOf(TargetExpression left, TargetType right) implements TargetExpression {
|
public record TargetInstanceOf(TargetExpression left, TargetExpression right) implements TargetExpression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TargetType type() {
|
public TargetType type() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package de.dhbwstuttgart.target.tree.expression;
|
package de.dhbwstuttgart.target.tree.expression;
|
||||||
|
|
||||||
public sealed interface TargetPattern extends TargetExpression permits ComplexPattern, Guard, SimplePattern {
|
public sealed interface TargetPattern extends TargetExpression permits TargetComplexPattern, TargetGuard, TargetTypePattern {
|
||||||
default String name() {
|
default String name() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ package de.dhbwstuttgart.target.tree.expression;
|
|||||||
|
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
|
|
||||||
public record SimplePattern(TargetType type, String name) implements TargetPattern {
|
public record TargetTypePattern(TargetType type, String name) implements TargetPattern {
|
||||||
}
|
}
|
@ -175,7 +175,7 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
// Blöcke inferieren:
|
// Blöcke inferieren:
|
||||||
ifStmt.then_block.accept(this);
|
ifStmt.then_block.accept(this);
|
||||||
// Beide Blöcke müssen den gleichen Supertyp haben, welcher den Rückgabetyp des If-Stmts darstellt
|
// Beide Blöcke müssen den gleichen Supertyp haben, welcher den Rückgabetyp des If-Stmts darstellt
|
||||||
constraintsSet.addUndConstraint(new Pair(ifStmt.else_block.getType(), ifStmt.getType(), PairOperator.SMALLERDOT));
|
//constraintsSet.addUndConstraint(new Pair(ifStmt.else_block.getType(), ifStmt.getType(), PairOperator.SMALLERDOT));
|
||||||
if (ifStmt.else_block != null) {
|
if (ifStmt.else_block != null) {
|
||||||
ifStmt.else_block.accept(this);
|
ifStmt.else_block.accept(this);
|
||||||
constraintsSet.addUndConstraint(new Pair(ifStmt.else_block.getType(), ifStmt.getType(), PairOperator.SMALLERDOT));
|
constraintsSet.addUndConstraint(new Pair(ifStmt.else_block.getType(), ifStmt.getType(), PairOperator.SMALLERDOT));
|
||||||
@ -185,7 +185,7 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InstanceOf instanceOf) {
|
public void visit(InstanceOf instanceOf) {
|
||||||
throw new NotImplementedException();
|
//throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -684,4 +684,11 @@ public class TestComplete {
|
|||||||
assertEquals(main.invoke(instance, "TEST"), 3);
|
assertEquals(main.invoke(instance, "TEST"), 3);
|
||||||
assertEquals(main.invoke(instance, "awawa"), 4);
|
assertEquals(main.invoke(instance, "awawa"), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstanceOfPattern() throws Exception {
|
||||||
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "InstanceOf.jav");
|
||||||
|
var clazz = classFiles.get("InstanceOf");
|
||||||
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,15 +237,15 @@ public class TestCodegen {
|
|||||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "SwitchEnhanced");
|
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 SimplePattern(TargetType.String, "aString")), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetTypePattern(TargetType.String, "aString")), new TargetBlock(
|
||||||
List.of(new TargetYield(new TargetLiteral.IntLiteral(0)))
|
List.of(new TargetYield(new TargetLiteral.IntLiteral(0)))
|
||||||
), false),
|
), false),
|
||||||
new TargetSwitch.Case(List.of(
|
new TargetSwitch.Case(List.of(
|
||||||
new Guard(new SimplePattern(TargetType.Integer, "i"), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)))
|
new TargetGuard(new TargetTypePattern(TargetType.Integer, "i"), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)))
|
||||||
), new TargetBlock(
|
), new TargetBlock(
|
||||||
List.of(new TargetLiteral.IntLiteral(3))
|
List.of(new TargetLiteral.IntLiteral(3))
|
||||||
), true),
|
), true),
|
||||||
new TargetSwitch.Case(List.of(new SimplePattern(TargetType.Integer, "anInteger")), new TargetBlock(
|
new TargetSwitch.Case(List.of(new TargetTypePattern(TargetType.Integer, "anInteger")), new TargetBlock(
|
||||||
List.of(new TargetLiteral.IntLiteral(1))
|
List.of(new TargetLiteral.IntLiteral(1))
|
||||||
), true)
|
), true)
|
||||||
), new TargetSwitch.Case(new TargetBlock(
|
), new TargetSwitch.Case(new TargetBlock(
|
||||||
|
Loading…
Reference in New Issue
Block a user