johns-branch #12
@ -2,11 +2,14 @@ package ast.block;
|
|||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
import ast.statement.StatementNode;
|
import ast.statement.StatementNode;
|
||||||
|
import semantic.SemanticVisitor;
|
||||||
|
import typechecker.TypeCheckResult;
|
||||||
|
import visitor.Visitable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BlockNode implements ASTNode {
|
public class BlockNode implements ASTNode, Visitable {
|
||||||
public List<StatementNode> statements = new ArrayList<>();
|
public List<StatementNode> statements = new ArrayList<>();
|
||||||
|
|
||||||
public BlockNode() {}
|
public BlockNode() {}
|
||||||
@ -14,4 +17,10 @@ public class BlockNode implements ASTNode {
|
|||||||
public void addStatement(StatementNode statement) {
|
public void addStatement(StatementNode statement) {
|
||||||
statements.add(statement);
|
statements.add(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult accept(SemanticVisitor visitor) {
|
||||||
|
return visitor.analyze(this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
package ast.expression;
|
package ast.expression;
|
||||||
|
|
||||||
public class ExpressionNode {
|
import ast.ASTNode;
|
||||||
|
|
||||||
|
public class ExpressionNode implements ASTNode {
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import ast.type.ValueNode;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class UnaryExpressionNode implements ASTNode {
|
public class UnaryExpressionNode extends ExpressionNode {
|
||||||
String thisExp;
|
String thisExp;
|
||||||
String identifier;
|
String identifier;
|
||||||
MemberAccessNode memberAccess;
|
MemberAccessNode memberAccess;
|
||||||
|
@ -9,14 +9,15 @@ import semantic.SemanticVisitor;
|
|||||||
import typechecker.TypeCheckResult;
|
import typechecker.TypeCheckResult;
|
||||||
import visitor.Visitable;
|
import visitor.Visitable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MethodNode implements MemberNode, Visitable {
|
public class MethodNode implements MemberNode, Visitable {
|
||||||
AccessModifierNode accesModifier;
|
AccessModifierNode accesModifier;
|
||||||
TypeNode type;
|
public TypeNode type;
|
||||||
Boolean voidType;
|
Boolean voidType;
|
||||||
String identifier;
|
public String identifier;
|
||||||
List<ParameterNode> parameters;
|
private List<ParameterNode> parameters = new ArrayList<>();
|
||||||
public BlockNode block;
|
public BlockNode block;
|
||||||
|
|
||||||
public MethodNode() {}
|
public MethodNode() {}
|
||||||
@ -37,24 +38,23 @@ public class MethodNode implements MemberNode, Visitable {
|
|||||||
this.parameters.add(parameter);
|
this.parameters.add(parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public List<ParameterNode> getParameters() {
|
||||||
public boolean isSame(MethodNode methodNode){
|
return parameters;
|
||||||
boolean isSame = false;
|
|
||||||
if(methodNode.identifier.equals(identifier)){
|
|
||||||
if(parameters != null && methodNode.parameters != null){
|
|
||||||
if(parameters.parameters.size() == methodNode.parameters.parameters.size()){
|
|
||||||
for(int i = 0; i < parameters.parameters.size(); i++){
|
|
||||||
if(parameters.parameters.get(i).identifier.equals(methodNode.parameters.parameters.get(i).identifier)){
|
|
||||||
isSame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isSame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
public boolean isSame(MethodNode methodNode){
|
||||||
|
if (!this.identifier.equals(methodNode.identifier) || this.type.equals(methodNode.type)
|
||||||
|
|| this.getParameters().size() != methodNode.getParameters().size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < this.getParameters().size(); i++) {
|
||||||
|
if (this.getParameters().get(i).type.equals(methodNode.getParameters().get(i).type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypeCheckResult accept(SemanticVisitor visitor) {
|
public TypeCheckResult accept(SemanticVisitor visitor) {
|
||||||
|
@ -2,13 +2,22 @@ package ast.parameter;
|
|||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
import ast.type.TypeNode;
|
import ast.type.TypeNode;
|
||||||
|
import semantic.SemanticVisitor;
|
||||||
|
import typechecker.TypeCheckResult;
|
||||||
|
import visitor.Visitable;
|
||||||
|
|
||||||
public class ParameterNode implements ASTNode {
|
public class ParameterNode implements ASTNode, Visitable {
|
||||||
TypeNode type;
|
public TypeNode type;
|
||||||
String identifier;
|
public String identifier;
|
||||||
|
|
||||||
public ParameterNode(TypeNode type, String identifier) {
|
public ParameterNode(TypeNode type, String identifier) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult accept(SemanticVisitor visitor) {
|
||||||
|
return visitor.analyze(this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import ast.ASTNode;
|
|||||||
import ast.expression.ExpressionNode;
|
import ast.expression.ExpressionNode;
|
||||||
import ast.type.TypeNode;
|
import ast.type.TypeNode;
|
||||||
|
|
||||||
public class LocalVariableDeclarationNode implements ASTNode {
|
public class LocalVariableDeclarationNode extends StatementNode {
|
||||||
TypeNode type;
|
TypeNode type;
|
||||||
String identifier;
|
String identifier;
|
||||||
String assign;
|
String assign;
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
package ast.statement;public class StatementNode {
|
package ast.statement;
|
||||||
|
|
||||||
|
import ast.ASTNode;
|
||||||
|
|
||||||
|
public class StatementNode implements ASTNode {
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,10 @@ public class TypeNode {
|
|||||||
this.type = EnumTypeNode.IDENTIFIER;
|
this.type = EnumTypeNode.IDENTIFIER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean equals(TypeNode o) {
|
||||||
|
return !o.type.equals(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
|
|||||||
} else {
|
} else {
|
||||||
MethodNode methodNode = new MethodNode((AccessModifierNode) visit(ctx.AccessModifier()), null, true, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
|
MethodNode methodNode = new MethodNode((AccessModifierNode) visit(ctx.AccessModifier()), null, true, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
|
||||||
for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) {
|
for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) {
|
||||||
methodNode.addParameter((ParameterNode) visit(parameter));
|
// methodNode.addParameter((ParameterNode) visit(parameter));
|
||||||
}
|
}
|
||||||
return methodNode;
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,14 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
|
import ast.block.BlockNode;
|
||||||
import ast.member.*;
|
import ast.member.*;
|
||||||
|
import ast.parameter.ParameterNode;
|
||||||
import ast.statement.*;
|
import ast.statement.*;
|
||||||
import ast.statement.ifstatement.IfStatementNode;
|
import ast.statement.ifstatement.IfStatementNode;
|
||||||
import ast.type.*;
|
import ast.type.*;
|
||||||
import semantic.context.Context;
|
import semantic.context.Context;
|
||||||
import semantic.exeptions.AlreadyDeclearedException;
|
import semantic.exeptions.AlreadyDeclearedException;
|
||||||
import semantic.exeptions.NotDeclearedException;
|
|
||||||
import semantic.exeptions.TypeMismatchException;
|
import semantic.exeptions.TypeMismatchException;
|
||||||
import typechecker.TypeCheckResult;
|
import typechecker.TypeCheckResult;
|
||||||
|
|
||||||
@ -96,10 +97,47 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
public TypeCheckResult analyze(MethodNode methodNode) {
|
public TypeCheckResult analyze(MethodNode methodNode) {
|
||||||
var valid = true;
|
var valid = true;
|
||||||
|
|
||||||
currentScope.pushScope();
|
for (var otherMethod : currentClass.getMethods()) {
|
||||||
|
if (Objects.equals(otherMethod , methodNode))
|
||||||
|
break;
|
||||||
|
if (otherMethod.isSame(methodNode)) {
|
||||||
|
errors.add(new AlreadyDeclearedException(
|
||||||
|
"Method " + methodNode.identifier + " is already defined in class "
|
||||||
|
+ currentClass.identifier));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentScope.pushScope();
|
||||||
|
for (var parameter : methodNode.getParameters()) {
|
||||||
|
var result = parameter.accept(this);
|
||||||
|
valid = valid && result.isValid();
|
||||||
|
currentScope.addLocalVar(parameter.identifier, parameter.type);
|
||||||
|
}
|
||||||
|
// Check if this method is already declared
|
||||||
|
|
||||||
|
// currentMethodReturnType = methodDecl.getType();
|
||||||
|
// currentNullType = currentMethodReturnType; // Solange nicht in einem Assign oder Methoden-Aufruf dieser Typ
|
||||||
|
|
||||||
|
// gesetzt ist, ist dieser der Rückgabewert der Methode
|
||||||
|
var result = methodNode.block.accept(this);
|
||||||
|
valid = valid && result.isValid();
|
||||||
currentScope.popScope();
|
currentScope.popScope();
|
||||||
return new TypeCheckResult(valid, null);
|
var resultType = result.getType();
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (resultType == null) {
|
||||||
|
resultType = new BaseType(Primitives.VOID);
|
||||||
|
}
|
||||||
|
if (!resultType.equals(methodDecl.getType())) {
|
||||||
|
errors.add(new TypeMismatchException("Method-Declaration " + methodDecl.getIdentifier() + " with type "
|
||||||
|
+ methodDecl.getType() + " has at least one Mismatching return Type:"
|
||||||
|
+ TypeHelper.generateLocationString(methodDecl.line, methodDecl.column, fileName)));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
return new TypeCheckResult(valid, resultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -128,4 +166,14 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult analyze(ParameterNode toCheck) {
|
||||||
|
return new TypeCheckResult(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult analyze(BlockNode toCheck) {
|
||||||
|
return new TypeCheckResult(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,7 +2,9 @@ package semantic;
|
|||||||
|
|
||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
|
import ast.block.BlockNode;
|
||||||
import ast.member.*;
|
import ast.member.*;
|
||||||
|
import ast.parameter.ParameterNode;
|
||||||
import ast.statement.*;
|
import ast.statement.*;
|
||||||
import ast.statement.ifstatement.*;
|
import ast.statement.ifstatement.*;
|
||||||
import typechecker.TypeCheckResult;
|
import typechecker.TypeCheckResult;
|
||||||
@ -23,4 +25,9 @@ public interface SemanticVisitor {
|
|||||||
|
|
||||||
TypeCheckResult analyze(WhileStatementNode toCheck);
|
TypeCheckResult analyze(WhileStatementNode toCheck);
|
||||||
|
|
||||||
|
TypeCheckResult analyze(ParameterNode toCheck);
|
||||||
|
|
||||||
|
TypeCheckResult analyze(BlockNode toCheck);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -2,7 +2,13 @@ package semantic;
|
|||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
import ast.block.BlockNode;
|
import ast.block.BlockNode;
|
||||||
|
import ast.member.FieldNode;
|
||||||
import ast.member.MethodNode;
|
import ast.member.MethodNode;
|
||||||
|
import ast.parameter.ParameterNode;
|
||||||
|
import ast.statement.StatementNode;
|
||||||
|
import ast.statement.statementexpression.NewDeclarationStatementExpressionNode;
|
||||||
|
import ast.type.AccessModifierNode;
|
||||||
|
import ast.type.TypeNode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -11,26 +17,94 @@ import static parser.generated.SimpleJavaParser.Identifier;
|
|||||||
|
|
||||||
public class Mocker {
|
public class Mocker {
|
||||||
|
|
||||||
public static ProgramNode mockCorrectClass(){
|
public static ASTNode mockTwoSameFields(){
|
||||||
ProgramNode p = new ProgramNode();
|
ProgramNode p = new ProgramNode();
|
||||||
|
|
||||||
ClassNode c = new ClassNode();
|
ClassNode c = new ClassNode();
|
||||||
c.identifier = "testClass";
|
c.identifier = "testClass";
|
||||||
|
|
||||||
MethodNode m = new MethodNode();
|
FieldNode f1 = new FieldNode(new AccessModifierNode("public"), new TypeNode("int"), "a");
|
||||||
|
|
||||||
BlockNode b = new BlockNode();
|
c.members.add(f1);
|
||||||
|
|
||||||
|
FieldNode f2 = new FieldNode(new AccessModifierNode("public"), new TypeNode("int"), "a");
|
||||||
|
|
||||||
|
c.members.add(f2);
|
||||||
// b.statements.add();
|
|
||||||
|
|
||||||
m.block = b;
|
|
||||||
|
|
||||||
c.members.add(m);
|
|
||||||
|
|
||||||
p.classes.add(c);
|
p.classes.add(c);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ASTNode mockSimpleMethod(){
|
||||||
|
ProgramNode p = new ProgramNode();
|
||||||
|
|
||||||
|
ClassNode c = new ClassNode();
|
||||||
|
|
||||||
|
MethodNode methodNode = new MethodNode();
|
||||||
|
|
||||||
|
//Parameter
|
||||||
|
ParameterNode parameterNode = new ParameterNode(new TypeNode("int"), "a");
|
||||||
|
|
||||||
|
methodNode.addParameter(parameterNode);
|
||||||
|
|
||||||
|
//Statements
|
||||||
|
|
||||||
|
//Block
|
||||||
|
methodNode.block = new BlockNode();
|
||||||
|
|
||||||
|
c.members.add(methodNode);
|
||||||
|
|
||||||
|
p.classes.add(c);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ASTNode mockTwoSameMethods(){
|
||||||
|
ProgramNode p = new ProgramNode();
|
||||||
|
|
||||||
|
ClassNode c = new ClassNode();
|
||||||
|
|
||||||
|
MethodNode methodNode = new MethodNode();
|
||||||
|
methodNode.block = new BlockNode();
|
||||||
|
methodNode.type = new TypeNode("int");
|
||||||
|
|
||||||
|
methodNode.identifier = "testMethod";
|
||||||
|
|
||||||
|
c.members.add(methodNode);
|
||||||
|
|
||||||
|
MethodNode methodNode1 = new MethodNode();
|
||||||
|
methodNode1.block = new BlockNode();
|
||||||
|
methodNode1.type = new TypeNode("int");
|
||||||
|
|
||||||
|
methodNode1.identifier = "testMethod";
|
||||||
|
|
||||||
|
c.members.add(methodNode1);
|
||||||
|
|
||||||
|
p.classes.add(c);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ASTNode mockTwoDifferentMethods(){
|
||||||
|
ProgramNode p = new ProgramNode();
|
||||||
|
|
||||||
|
ClassNode c = new ClassNode();
|
||||||
|
|
||||||
|
MethodNode methodNode = new MethodNode();
|
||||||
|
methodNode.block = new BlockNode();
|
||||||
|
methodNode.identifier = "testMethod";
|
||||||
|
|
||||||
|
c.members.add(methodNode);
|
||||||
|
|
||||||
|
MethodNode methodNode1 = new MethodNode();
|
||||||
|
methodNode1.block = new BlockNode();
|
||||||
|
methodNode1.identifier = "testMethod1";
|
||||||
|
|
||||||
|
c.members.add(methodNode1);
|
||||||
|
|
||||||
|
p.classes.add(c);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,63 @@
|
|||||||
package semantic;
|
package semantic;
|
||||||
|
|
||||||
|
import ast.ASTNode;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import semantic.exeptions.AlreadyDeclearedException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class SemanticTest {
|
public class SemanticTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup(){
|
||||||
|
SemanticAnalyzer.clearAnalyzier();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void correctClass(){
|
public void twoFieldsSameName() {
|
||||||
|
|
||||||
|
ASTNode ast = Mocker.mockTwoSameFields();
|
||||||
|
|
||||||
|
ASTNode tast = SemanticAnalyzer.generateTast(ast);
|
||||||
|
|
||||||
|
assertEquals(SemanticAnalyzer.errors.size(), 1);
|
||||||
|
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
|
||||||
|
assertNull(tast);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void simpleMethod(){
|
||||||
|
|
||||||
|
ASTNode ast = Mocker.mockSimpleMethod();
|
||||||
|
|
||||||
|
ASTNode tast = SemanticAnalyzer.generateTast(ast);
|
||||||
|
|
||||||
|
assertEquals(SemanticAnalyzer.errors.size(), 0);
|
||||||
|
assertNotNull(tast);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoSameMethods(){
|
||||||
|
ASTNode ast = Mocker.mockTwoSameMethods();
|
||||||
|
|
||||||
|
ASTNode tast = SemanticAnalyzer.generateTast(ast);
|
||||||
|
|
||||||
|
assertEquals(1, SemanticAnalyzer.errors.size());
|
||||||
|
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
|
||||||
|
assertNull(tast);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void twoDifferentMethods(){
|
||||||
|
ASTNode ast = Mocker.mockTwoDifferentMethods();
|
||||||
|
|
||||||
|
ASTNode tast = SemanticAnalyzer.generateTast(ast);
|
||||||
|
|
||||||
|
assertEquals(SemanticAnalyzer.errors.size(), 0);
|
||||||
|
assertNotNull(tast);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package semantic.endToTAST;
|
package semantic.endToTAST;
|
||||||
|
|
||||||
|
import ast.ASTNode;
|
||||||
import ast.ProgramNode;
|
import ast.ProgramNode;
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
@ -9,10 +10,13 @@ import org.junit.jupiter.api.Test;
|
|||||||
import parser.astBuilder.ASTBuilder;
|
import parser.astBuilder.ASTBuilder;
|
||||||
import parser.generated.SimpleJavaLexer;
|
import parser.generated.SimpleJavaLexer;
|
||||||
import parser.generated.SimpleJavaParser;
|
import parser.generated.SimpleJavaParser;
|
||||||
|
import semantic.SemanticAnalyzer;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class CorrectTest {
|
public class CorrectTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -36,6 +40,12 @@ public class CorrectTest {
|
|||||||
|
|
||||||
System.out.println("Test");
|
System.out.println("Test");
|
||||||
|
|
||||||
|
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
|
||||||
|
|
||||||
|
System.out.println("Errors: " + SemanticAnalyzer.errors.size());
|
||||||
|
|
||||||
|
assertEquals(SemanticAnalyzer.errors.size(), 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
public class Test {
|
public class Test {
|
||||||
|
|
||||||
public void test(){
|
public boolean b;
|
||||||
|
public boolean b;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user