Tphs einer Methode, die groesser als Tphs der Felder, werden als Class-Generics definiert
This commit is contained in:
parent
e380c77ce6
commit
b71efd65a0
@ -6,6 +6,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
||||||
@ -62,6 +63,8 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
|
|
||||||
private String superClass;
|
private String superClass;
|
||||||
|
|
||||||
|
private ArrayList<TypePlaceholder> tphsClass;
|
||||||
|
|
||||||
// stores parameter, local vars and the next index on the local variable table, which use for aload_i, astore_i,...
|
// stores parameter, local vars and the next index on the local variable table, which use for aload_i, astore_i,...
|
||||||
HashMap<String, Integer> paramsAndLocals = new HashMap<>();
|
HashMap<String, Integer> paramsAndLocals = new HashMap<>();
|
||||||
// stores generics and their bounds of class
|
// stores generics and their bounds of class
|
||||||
@ -138,12 +141,28 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
|
|
||||||
getCommonTPHS(tphExtractor);
|
getCommonTPHS(tphExtractor);
|
||||||
|
|
||||||
ArrayList<TypePlaceholder> tphsClass = new ArrayList<>();
|
tphsClass = new ArrayList<>();
|
||||||
for(TypePlaceholder t : tphExtractor.allTPHS.keySet()) {
|
for(TypePlaceholder t : tphExtractor.allTPHS.keySet()) {
|
||||||
if(!tphExtractor.allTPHS.get(t))
|
if(!tphExtractor.allTPHS.get(t))
|
||||||
tphsClass.add(t);
|
tphsClass.add(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayList<TPHConstraint> consClass = new ArrayList<>();
|
||||||
|
for(TPHConstraint cons : tphExtractor.allCons) {
|
||||||
|
TypePlaceholder right = null;
|
||||||
|
for(TypePlaceholder tph : tphsClass) {
|
||||||
|
if(cons.getLeft().equals(tph.getName())) {
|
||||||
|
|
||||||
|
consClass.add(cons);
|
||||||
|
right = getTPH(cons.getRight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(right != null) {
|
||||||
|
tphsClass.add(right);
|
||||||
|
removeFromMethod(right.getName());
|
||||||
|
right = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
String sig = null;
|
String sig = null;
|
||||||
/* if class has generics then creates signature
|
/* if class has generics then creates signature
|
||||||
* Signature looks like:
|
* Signature looks like:
|
||||||
@ -152,7 +171,7 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
if(classOrInterface.getGenerics().iterator().hasNext() || !commonPairs.isEmpty() ||
|
if(classOrInterface.getGenerics().iterator().hasNext() || !commonPairs.isEmpty() ||
|
||||||
classOrInterface.getSuperClass().acceptTV(new TypeToSignature()).contains("<")
|
classOrInterface.getSuperClass().acceptTV(new TypeToSignature()).contains("<")
|
||||||
|| !tphsClass.isEmpty()) {
|
|| !tphsClass.isEmpty()) {
|
||||||
Signature signature = new Signature(classOrInterface, genericsAndBounds,commonPairs,tphsClass);
|
Signature signature = new Signature(classOrInterface, genericsAndBounds,commonPairs,tphsClass, consClass);
|
||||||
sig = signature.toString();
|
sig = signature.toString();
|
||||||
System.out.println("Signature: => " + sig);
|
System.out.println("Signature: => " + sig);
|
||||||
}
|
}
|
||||||
@ -184,6 +203,31 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeFromMethod(String name) {
|
||||||
|
for(MethodAndTPH m : tphExtractor.ListOfMethodsAndTph) {
|
||||||
|
ArrayList<String> toRemove = new ArrayList<>();
|
||||||
|
for(String tph : m.getTphs()) {
|
||||||
|
if(tph.equals(name)) {
|
||||||
|
toRemove.add(tph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!toRemove.isEmpty()) {
|
||||||
|
m.getTphs().removeAll(toRemove);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypePlaceholder getTPH(String name) {
|
||||||
|
for(TypePlaceholder tph: tphExtractor.allTPHS.keySet()) {
|
||||||
|
if(tph.getName().equals(name))
|
||||||
|
return tph;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException("TPH "+name +" does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
private void getCommonTPHS(TPHExtractor tphExtractor) {
|
private void getCommonTPHS(TPHExtractor tphExtractor) {
|
||||||
// Gemeinsame TPHs
|
// Gemeinsame TPHs
|
||||||
ArrayList<TypePlaceholder> cTPHs = new ArrayList<>();
|
ArrayList<TypePlaceholder> cTPHs = new ArrayList<>();
|
||||||
@ -236,7 +280,7 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
}
|
}
|
||||||
String sig = null;
|
String sig = null;
|
||||||
if(hasGen) {
|
if(hasGen) {
|
||||||
HashMap<TPHConstraint, HashSet<String>> constraints = Simplify.simplifyConstraints(field.name, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> constraints = Simplify.simplifyConstraints(field.name, tphExtractor,tphsClass);
|
||||||
Signature signature = new Signature(field, genericsAndBounds,methodParamsAndTypes,resultSet,constraints);
|
Signature signature = new Signature(field, genericsAndBounds,methodParamsAndTypes,resultSet,constraints);
|
||||||
sig = signature.toString();
|
sig = signature.toString();
|
||||||
}
|
}
|
||||||
@ -315,7 +359,7 @@ public class BytecodeGen implements ASTVisitor {
|
|||||||
System.out.println("ALL CONST: " + tphExtractor.allCons.size());
|
System.out.println("ALL CONST: " + tphExtractor.allCons.size());
|
||||||
tphExtractor.allCons.forEach(c->System.out.println(c.toString()));
|
tphExtractor.allCons.forEach(c->System.out.println(c.toString()));
|
||||||
System.out.println("----------------");
|
System.out.println("----------------");
|
||||||
HashMap<TPHConstraint, HashSet<String>> constraints = Simplify.simplifyConstraints(method.name, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> constraints = Simplify.simplifyConstraints(method.name, tphExtractor, tphsClass);
|
||||||
// ArrayList<GenericInsertPair> pairs = simplifyPairs(method.name,tphExtractor.allPairs,tphExtractor.allCons);
|
// ArrayList<GenericInsertPair> pairs = simplifyPairs(method.name,tphExtractor.allPairs,tphExtractor.allCons);
|
||||||
Signature signature = new Signature(method, genericsAndBoundsMethod, genericsAndBounds,methodParamsAndTypes,resultSet,constraints);
|
Signature signature = new Signature(method, genericsAndBoundsMethod, genericsAndBounds,methodParamsAndTypes,resultSet,constraints);
|
||||||
sig = signature.toString();
|
sig = signature.toString();
|
||||||
|
@ -708,7 +708,13 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
@Override
|
@Override
|
||||||
public void visit(FieldVar fieldVar) {
|
public void visit(FieldVar fieldVar) {
|
||||||
fieldName = fieldVar.fieldVarName;
|
fieldName = fieldVar.fieldVarName;
|
||||||
fieldDesc = "L" + getResolvedType(fieldVar.getType()) + ";";
|
fieldDesc = "L";
|
||||||
|
if(resultSet.resolveType(fieldVar.getType()).resolvedType instanceof TypePlaceholder) {
|
||||||
|
fieldDesc += Type.getInternalName(Object.class);
|
||||||
|
} else {
|
||||||
|
fieldDesc += resultSet.resolveType(fieldVar.getType()).resolvedType.acceptTV(new TypeToDescriptor());
|
||||||
|
}
|
||||||
|
fieldDesc +=";";
|
||||||
|
|
||||||
fieldVar.receiver.accept(this);
|
fieldVar.receiver.accept(this);
|
||||||
// test (if)
|
// test (if)
|
||||||
@ -1405,9 +1411,18 @@ public class BytecodeGenMethod implements StatementVisitor {
|
|||||||
// array slot onto the top of the operand stack.
|
// array slot onto the top of the operand stack.
|
||||||
assignLeftSide.field.receiver.accept(this);
|
assignLeftSide.field.receiver.accept(this);
|
||||||
this.rightSideTemp.accept(this);
|
this.rightSideTemp.accept(this);
|
||||||
|
String fDesc = "L";
|
||||||
|
if(resultSet.resolveType(assignLeftSide.field.getType()).resolvedType instanceof TypePlaceholder) {
|
||||||
|
fDesc += Type.getInternalName(Object.class);
|
||||||
|
} else {
|
||||||
|
fDesc += resultSet.resolveType(assignLeftSide.field.getType()).resolvedType.acceptTV(new TypeToDescriptor());
|
||||||
|
}
|
||||||
|
fDesc +=";";
|
||||||
|
|
||||||
|
|
||||||
System.out.println("Receiver = " + getResolvedType(assignLeftSide.field.receiver.getType()));
|
System.out.println("Receiver = " + getResolvedType(assignLeftSide.field.receiver.getType()));
|
||||||
mv.visitFieldInsn(Opcodes.PUTFIELD, getResolvedType(assignLeftSide.field.receiver.getType()),
|
mv.visitFieldInsn(Opcodes.PUTFIELD, getResolvedType(assignLeftSide.field.receiver.getType()),
|
||||||
assignLeftSide.field.fieldVarName, "L"+getResolvedType(assignLeftSide.field.getType())+";");
|
assignLeftSide.field.fieldVarName, fDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,13 +42,15 @@ public class Signature {
|
|||||||
private ArrayList<GenericInsertPair> commonPairs;
|
private ArrayList<GenericInsertPair> commonPairs;
|
||||||
private HashMap<TPHConstraint,HashSet<String>> methodConstraints;
|
private HashMap<TPHConstraint,HashSet<String>> methodConstraints;
|
||||||
private ArrayList<TypePlaceholder> tphsClass;
|
private ArrayList<TypePlaceholder> tphsClass;
|
||||||
|
private ArrayList<TPHConstraint> consClass;
|
||||||
|
|
||||||
public Signature(ClassOrInterface classOrInterface, HashMap<String, String> genericsAndBounds,
|
public Signature(ClassOrInterface classOrInterface, HashMap<String, String> genericsAndBounds,
|
||||||
ArrayList<GenericInsertPair> commonPairs, ArrayList<TypePlaceholder> tphsClass) {
|
ArrayList<GenericInsertPair> commonPairs, ArrayList<TypePlaceholder> tphsClass, ArrayList<TPHConstraint> consClass) {
|
||||||
this.classOrInterface = classOrInterface;
|
this.classOrInterface = classOrInterface;
|
||||||
this.genericsAndBounds = genericsAndBounds;
|
this.genericsAndBounds = genericsAndBounds;
|
||||||
this.commonPairs = commonPairs;
|
this.commonPairs = commonPairs;
|
||||||
this.tphsClass = tphsClass;
|
this.tphsClass = tphsClass;
|
||||||
|
this.consClass = consClass;
|
||||||
sw = new SignatureWriter();
|
sw = new SignatureWriter();
|
||||||
createSignatureForClassOrInterface();
|
createSignatureForClassOrInterface();
|
||||||
}
|
}
|
||||||
@ -370,7 +372,37 @@ public class Signature {
|
|||||||
GenericTypeVar g = itr.next();
|
GenericTypeVar g = itr.next();
|
||||||
getBoundsOfTypeVar(g,genericsAndBounds);
|
getBoundsOfTypeVar(g,genericsAndBounds);
|
||||||
}
|
}
|
||||||
if(!commonPairs.isEmpty()) {
|
|
||||||
|
if(!consClass.isEmpty()) {
|
||||||
|
ArrayList<String> types = new ArrayList<>();
|
||||||
|
ArrayList<String> superTypes = new ArrayList<>();
|
||||||
|
|
||||||
|
for(TPHConstraint cons : consClass) {
|
||||||
|
types.add(cons.getLeft());
|
||||||
|
superTypes.add(cons.getRight());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(TPHConstraint cons : consClass) {
|
||||||
|
String t = cons.getLeft()+"$";
|
||||||
|
String bound = cons.getRight()+"$";
|
||||||
|
sw.visitFormalTypeParameter(t);
|
||||||
|
sw.visitClassBound().visitTypeVariable(bound);
|
||||||
|
genericsAndBounds.put(t, bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(TPHConstraint cons : consClass) {
|
||||||
|
if(!types.contains(cons.getRight())) {
|
||||||
|
String t = cons.getRight()+"$";
|
||||||
|
String bound = Type.getInternalName(Object.class);
|
||||||
|
sw.visitFormalTypeParameter(t);
|
||||||
|
sw.visitClassBound().visitClassType(bound);
|
||||||
|
genericsAndBounds.put(t, bound);
|
||||||
|
sw.visitClassBound().visitEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*if(!commonPairs.isEmpty()) {
|
||||||
ArrayList<TypePlaceholder> types = new ArrayList<>();
|
ArrayList<TypePlaceholder> types = new ArrayList<>();
|
||||||
ArrayList<TypePlaceholder> superTypes = new ArrayList<>();
|
ArrayList<TypePlaceholder> superTypes = new ArrayList<>();
|
||||||
|
|
||||||
@ -405,7 +437,7 @@ public class Signature {
|
|||||||
sw.visitClassBound().visitClassType(bound);
|
sw.visitClassBound().visitClassType(bound);
|
||||||
genericsAndBounds.put(n, bound);
|
genericsAndBounds.put(n, bound);
|
||||||
sw.visitClassBound().visitEnd();
|
sw.visitClassBound().visitEnd();
|
||||||
}
|
}*/
|
||||||
String sClass = classOrInterface.getSuperClass().acceptTV(new TypeToSignature());
|
String sClass = classOrInterface.getSuperClass().acceptTV(new TypeToSignature());
|
||||||
sw.visitSuperclass().visitClassType(sClass.substring(1, sClass.length()-1));
|
sw.visitSuperclass().visitClassType(sClass.substring(1, sClass.length()-1));
|
||||||
sw.visitEnd();
|
sw.visitEnd();
|
||||||
|
@ -15,8 +15,8 @@ import de.dhbwstuttgart.bytecode.constraint.TPHConstraint.Relation;
|
|||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
||||||
|
|
||||||
public class Simplify {
|
public class Simplify {
|
||||||
private static int a = 6;
|
|
||||||
public static HashMap<TPHConstraint, HashSet<String>> simplifyConstraints(String name, TPHExtractor tphExtractor) {
|
public static HashMap<TPHConstraint, HashSet<String>> simplifyConstraints(String name, TPHExtractor tphExtractor, ArrayList<TypePlaceholder> tphsClass) {
|
||||||
// 1. check if there are any simple cycles like L<R and R<L:
|
// 1. check if there are any simple cycles like L<R and R<L:
|
||||||
// a) yes => set L=R and:
|
// a) yes => set L=R and:
|
||||||
// * remove both constraints
|
// * remove both constraints
|
||||||
|
52
test/bytecode/FieldTph2Test.java
Normal file
52
test/bytecode/FieldTph2Test.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package bytecode;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.core.JavaTXCompiler;
|
||||||
|
|
||||||
|
public class FieldTph2Test {
|
||||||
|
|
||||||
|
private static String path;
|
||||||
|
private static File fileToTest;
|
||||||
|
private static JavaTXCompiler compiler;
|
||||||
|
private static ClassLoader loader;
|
||||||
|
private static Class<?> classToTest;
|
||||||
|
private static String pathToClassFile;
|
||||||
|
private static Object instanceOfClass;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpBeforeClass() throws Exception {
|
||||||
|
path = System.getProperty("user.dir")+"/test/bytecode/javFiles/FieldTph2.jav";
|
||||||
|
fileToTest = new File(path);
|
||||||
|
compiler = new JavaTXCompiler(fileToTest);
|
||||||
|
compiler.generateBytecode(System.getProperty("user.dir")+"/testBytecode/generatedBC/");
|
||||||
|
pathToClassFile = System.getProperty("user.dir")+"/testBytecode/generatedBC/";
|
||||||
|
loader = new URLClassLoader(new URL[] {new URL("file://"+pathToClassFile)});
|
||||||
|
classToTest = loader.loadClass("FieldTph2");
|
||||||
|
instanceOfClass = classToTest.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
Field a = classToTest.getDeclaredField("a");
|
||||||
|
a.setAccessible(true);
|
||||||
|
|
||||||
|
Method m2 = classToTest.getDeclaredMethod("m2", Object.class);
|
||||||
|
m2.invoke(instanceOfClass, 1);
|
||||||
|
|
||||||
|
Method m = classToTest.getDeclaredMethod("m", Object.class);
|
||||||
|
Object result = m.invoke(instanceOfClass, 1);
|
||||||
|
|
||||||
|
assertEquals(1,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
test/bytecode/javFiles/FieldTph2.jav
Normal file
12
test/bytecode/javFiles/FieldTph2.jav
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
public class FieldTph2 {
|
||||||
|
a;
|
||||||
|
|
||||||
|
m(b){
|
||||||
|
b = a;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
m2(c){
|
||||||
|
a = c;
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ package bytecode.simplifyalgo;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ public class CycleTest {
|
|||||||
TPHConstraint k = new ExtendsConstraint("A", Type.getInternalName(Object.class), Relation.EXTENDS);
|
TPHConstraint k = new ExtendsConstraint("A", Type.getInternalName(Object.class), Relation.EXTENDS);
|
||||||
result.put(k, equals);
|
result.put(k, equals);
|
||||||
|
|
||||||
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor,new ArrayList<>());
|
||||||
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
||||||
assertTrue(areEquals);
|
assertTrue(areEquals);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package bytecode.simplifyalgo;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ public class SameLeftSide {
|
|||||||
|
|
||||||
result.put(b, hs);
|
result.put(b, hs);
|
||||||
|
|
||||||
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor,new ArrayList<>());
|
||||||
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
||||||
assertTrue(areEquals);
|
assertTrue(areEquals);
|
||||||
}
|
}
|
||||||
@ -93,7 +94,7 @@ public class SameLeftSide {
|
|||||||
hs.add("B");
|
hs.add("B");
|
||||||
result.put(c, hs);
|
result.put(c, hs);
|
||||||
|
|
||||||
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName2, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName2, tphExtractor,new ArrayList<>());
|
||||||
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
boolean areEquals = SimpleCycle.areMapsEqual(result, sim);
|
||||||
assertTrue(areEquals);
|
assertTrue(areEquals);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package bytecode.simplifyalgo;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ public class SimpleCycle {
|
|||||||
TPHConstraint k = new ExtendsConstraint("B", Type.getInternalName(Object.class), Relation.EXTENDS);
|
TPHConstraint k = new ExtendsConstraint("B", Type.getInternalName(Object.class), Relation.EXTENDS);
|
||||||
result.put(k, equals);
|
result.put(k, equals);
|
||||||
|
|
||||||
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor);
|
HashMap<TPHConstraint, HashSet<String>> sim = Simplify.simplifyConstraints(methName, tphExtractor,new ArrayList<>());
|
||||||
boolean areEquals = areMapsEqual(result, sim);
|
boolean areEquals = areMapsEqual(result, sim);
|
||||||
assertTrue(areEquals);
|
assertTrue(areEquals);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user