8281462: Annotation toString output for enum not reusable for source input
Reviewed-by: mchung
This commit is contained in:
parent
4032fe76dc
commit
c3179a8760
src/java.base/share/classes/sun/reflect/annotation
test/jdk/java/lang
annotation
AnnotationToStringTest.java
AnnotationTypeMismatchException
TestConstructorParameterAnnotations.javatypeAnnotations
reflect/records
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -145,7 +145,9 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
private String toStringImpl() {
|
||||
StringBuilder result = new StringBuilder(128);
|
||||
result.append('@');
|
||||
result.append(type.getName());
|
||||
// Guard against null canonical name; shouldn't happen
|
||||
result.append(Objects.toString(type.getCanonicalName(),
|
||||
"<no canonical name>"));
|
||||
result.append('(');
|
||||
boolean firstMember = true;
|
||||
Set<Map.Entry<String, Object>> entries = memberValues.entrySet();
|
||||
@ -189,6 +191,10 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
return toSourceString((long) value);
|
||||
else if (type == Byte.class)
|
||||
return toSourceString((byte) value);
|
||||
else if (value instanceof Enum<?> v)
|
||||
// Predicate above covers enum constants, including
|
||||
// those with specialized class bodies.
|
||||
return toSourceString(v);
|
||||
else
|
||||
return value.toString();
|
||||
} else {
|
||||
@ -219,6 +225,10 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
stringStream =
|
||||
Arrays.stream((String[])value).
|
||||
map(AnnotationInvocationHandler::toSourceString);
|
||||
else if (type.getComponentType().isEnum())
|
||||
stringStream =
|
||||
Arrays.stream((Enum<?>[])value).
|
||||
map(AnnotationInvocationHandler::toSourceString);
|
||||
else
|
||||
stringStream = Arrays.stream((Object[])value).map(Objects::toString);
|
||||
|
||||
@ -231,15 +241,9 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
* string representation of an annotation.
|
||||
*/
|
||||
private static String toSourceString(Class<?> clazz) {
|
||||
Class<?> finalComponent = clazz;
|
||||
StringBuilder arrayBrackets = new StringBuilder();
|
||||
|
||||
while(finalComponent.isArray()) {
|
||||
finalComponent = finalComponent.getComponentType();
|
||||
arrayBrackets.append("[]");
|
||||
}
|
||||
|
||||
return finalComponent.getName() + arrayBrackets.toString() + ".class";
|
||||
// Guard against null canonical name; shouldn't happen
|
||||
return Objects.toString(clazz.getCanonicalName(),
|
||||
"<no canonical name>") + ".class";
|
||||
}
|
||||
|
||||
private static String toSourceString(float f) {
|
||||
@ -307,6 +311,10 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
return String.valueOf(ell) + "L";
|
||||
}
|
||||
|
||||
private static String toSourceString(Enum<?> enumConstant) {
|
||||
return enumConstant.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string suitable for use in the string representation
|
||||
* of an annotation.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,7 +31,8 @@
|
||||
// test/langtools/tools/javac/processing/model/element/AnnotationToStringTest.java
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Field;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@ -160,6 +161,11 @@ public class AnnotationToStringTest {
|
||||
}
|
||||
|
||||
static class ArrayAnnotationHost {
|
||||
@ExpectedString(
|
||||
"@EnumValue(NON_SEALED)") // toString and name differ
|
||||
@EnumValue(Modifier.NON_SEALED)
|
||||
public int f00;
|
||||
|
||||
@ExpectedString(
|
||||
"@BooleanArray({true, false, true})")
|
||||
@BooleanArray({true, false, true})
|
||||
@ -213,8 +219,8 @@ public class AnnotationToStringTest {
|
||||
public Class<?>[] f9;
|
||||
|
||||
@ExpectedString(
|
||||
"@EnumArray({SOURCE})")
|
||||
@EnumArray({RetentionPolicy.SOURCE})
|
||||
"@EnumArray({SEALED, NON_SEALED, PUBLIC})")
|
||||
@EnumArray({Modifier.SEALED, Modifier.NON_SEALED, Modifier.PUBLIC})
|
||||
public RetentionPolicy[] f10;
|
||||
}
|
||||
}
|
||||
@ -223,6 +229,11 @@ public class AnnotationToStringTest {
|
||||
|
||||
class Obj {}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface EnumValue {
|
||||
Modifier value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ExpectedString {
|
||||
String value();
|
||||
@ -285,7 +296,7 @@ class Obj {}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface EnumArray {
|
||||
RetentionPolicy[] value();
|
||||
Modifier[] value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -61,7 +61,7 @@ public class EnumTypeMismatchTest {
|
||||
} catch (AnnotationTypeMismatchException e) {
|
||||
if (!e.element().getName().equals("value")) {
|
||||
throw new IllegalStateException("Unexpected element: " + e.element());
|
||||
} else if (!e.foundType().equals("@" + AnAnnotation.class.getName() + "(" + AnEnum.VALUE.name() + ")")) {
|
||||
} else if (!e.foundType().equals("@" + AnAnnotation.class.getCanonicalName() + "(" + AnEnum.VALUE.name() + ")")) {
|
||||
throw new IllegalStateException("Unexpected type: " + e.foundType());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -129,21 +129,21 @@ public class TestConstructorParameterAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[], " +
|
||||
"[@TestConstructorParameterAnnotations$MarkerAnnotation(1)]]")
|
||||
"[@TestConstructorParameterAnnotations.MarkerAnnotation(1)]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(1)"})
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(1)"})
|
||||
public class NestedClass1 {
|
||||
public NestedClass1(@MarkerAnnotation(1) int parameter) {}
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[], " +
|
||||
"[@TestConstructorParameterAnnotations$MarkerAnnotation(2)], " +
|
||||
"[@TestConstructorParameterAnnotations.MarkerAnnotation(2)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(2)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(2)",
|
||||
"null"})
|
||||
public class NestedClass2 {
|
||||
public NestedClass2(@MarkerAnnotation(2) int parameter1,
|
||||
@ -152,11 +152,11 @@ public class TestConstructorParameterAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[], " +
|
||||
"[@TestConstructorParameterAnnotations$MarkerAnnotation(3)], " +
|
||||
"[@TestConstructorParameterAnnotations.MarkerAnnotation(3)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(3)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(3)",
|
||||
"null"})
|
||||
public class NestedClass3 {
|
||||
public <P> NestedClass3(@MarkerAnnotation(3) P parameter1,
|
||||
@ -165,11 +165,11 @@ public class TestConstructorParameterAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[], " +
|
||||
"[@TestConstructorParameterAnnotations$MarkerAnnotation(4)], " +
|
||||
"[@TestConstructorParameterAnnotations.MarkerAnnotation(4)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(4)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(4)",
|
||||
"null"})
|
||||
public class NestedClass4 {
|
||||
public <P, Q> NestedClass4(@MarkerAnnotation(4) P parameter1,
|
||||
@ -183,18 +183,18 @@ public class TestConstructorParameterAnnotations {
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[@TestConstructorParameterAnnotations$MarkerAnnotation(1)]]")
|
||||
"[[@TestConstructorParameterAnnotations.MarkerAnnotation(1)]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(1)"})
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(1)"})
|
||||
public static class StaticNestedClass1 {
|
||||
public StaticNestedClass1(@MarkerAnnotation(1) int parameter) {}
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[@TestConstructorParameterAnnotations$MarkerAnnotation(2)], " +
|
||||
"[[@TestConstructorParameterAnnotations.MarkerAnnotation(2)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(2)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(2)",
|
||||
"null"})
|
||||
public static class StaticNestedClass2 {
|
||||
public StaticNestedClass2(@MarkerAnnotation(2) int parameter1,
|
||||
@ -202,10 +202,10 @@ public class TestConstructorParameterAnnotations {
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[@TestConstructorParameterAnnotations$MarkerAnnotation(3)], " +
|
||||
"[[@TestConstructorParameterAnnotations.MarkerAnnotation(3)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(3)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(3)",
|
||||
"null"})
|
||||
public static class StaticNestedClass3 {
|
||||
public <P> StaticNestedClass3(@MarkerAnnotation(3) P parameter1,
|
||||
@ -213,10 +213,10 @@ public class TestConstructorParameterAnnotations {
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations(
|
||||
"[[@TestConstructorParameterAnnotations$MarkerAnnotation(4)], " +
|
||||
"[[@TestConstructorParameterAnnotations.MarkerAnnotation(4)], " +
|
||||
"[]]")
|
||||
@ExpectedParameterAnnotations({
|
||||
"@TestConstructorParameterAnnotations$MarkerAnnotation(4)",
|
||||
"@TestConstructorParameterAnnotations.MarkerAnnotation(4)",
|
||||
"null"})
|
||||
public static class StaticNestedClass4 {
|
||||
public <P, Q> StaticNestedClass4(@MarkerAnnotation(4) P parameter1,
|
||||
|
@ -64,31 +64,31 @@ public class GetAnnotatedNestedSuperclass {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
AnnotatedType x = Y.class.getAnnotatedSuperclass();
|
||||
assertEquals(Arrays.toString(x.getAnnotations()), "[@GetAnnotatedNestedSuperclass$A()]");
|
||||
assertEquals(Arrays.toString(x.getAnnotations()), "[@GetAnnotatedNestedSuperclass.A()]");
|
||||
AnnotatedParameterizedType xpt = (AnnotatedParameterizedType) x;
|
||||
{
|
||||
AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[0];
|
||||
assertEquals(
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$B()]");
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass.B()]");
|
||||
}
|
||||
{
|
||||
AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[1];
|
||||
assertEquals(
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$C()]");
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass.C()]");
|
||||
}
|
||||
{
|
||||
AnnotatedType arg = xpt.getAnnotatedActualTypeArguments()[2];
|
||||
assertEquals(
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass$D()]");
|
||||
Arrays.toString(arg.getAnnotations()), "[@GetAnnotatedNestedSuperclass.D()]");
|
||||
AnnotatedType nestedArg =
|
||||
((AnnotatedParameterizedType) arg).getAnnotatedActualTypeArguments()[0];
|
||||
assertEquals(
|
||||
Arrays.toString(nestedArg.getAnnotations()),
|
||||
"[@GetAnnotatedNestedSuperclass$E()]");
|
||||
"[@GetAnnotatedNestedSuperclass.E()]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(Object expected, Object actual) {
|
||||
private static void assertEquals(Object actual, Object expected) {
|
||||
if (!Objects.equals(expected, actual)) {
|
||||
throw new AssertionError("expected: " + expected + "; actual=" + actual);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -128,7 +128,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
@ExpectedGetParameterAnnotations("[[], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(1)"})
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(1)"})
|
||||
public class NestedClass1 {
|
||||
public NestedClass1(@MarkerTypeAnnotation(1) int parameter) {}
|
||||
}
|
||||
@ -136,7 +136,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
@ExpectedGetParameterAnnotations("[[], [], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(2)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(2)",
|
||||
"null"})
|
||||
public class NestedClass2 {
|
||||
public NestedClass2(@MarkerTypeAnnotation(2) int parameter1,
|
||||
@ -146,7 +146,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
@ExpectedGetParameterAnnotations("[[], [], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(3)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(3)",
|
||||
"null"})
|
||||
public class NestedClass3 {
|
||||
public <P> NestedClass3(@MarkerTypeAnnotation(3) P parameter1,
|
||||
@ -156,7 +156,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
@ExpectedGetParameterAnnotations("[[], [], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"null",
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(4)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(4)",
|
||||
"null"})
|
||||
public class NestedClass4 {
|
||||
public <P, Q> NestedClass4(@MarkerTypeAnnotation(4) P parameter1,
|
||||
@ -171,14 +171,14 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations("[[]]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(1)"})
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(1)"})
|
||||
public static class StaticNestedClass1 {
|
||||
public StaticNestedClass1(@MarkerTypeAnnotation(1) int parameter) {}
|
||||
}
|
||||
|
||||
@ExpectedGetParameterAnnotations("[[], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(2)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(2)",
|
||||
"null"})
|
||||
public static class StaticNestedClass2 {
|
||||
public StaticNestedClass2(@MarkerTypeAnnotation(2) int parameter1,
|
||||
@ -187,7 +187,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations("[[], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(3)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(3)",
|
||||
"null"})
|
||||
public static class StaticNestedClass3 {
|
||||
public <P> StaticNestedClass3(@MarkerTypeAnnotation(3) P parameter1,
|
||||
@ -196,7 +196,7 @@ public class TestConstructorParameterTypeAnnotations {
|
||||
|
||||
@ExpectedGetParameterAnnotations("[[], []]")
|
||||
@ExpectedParameterTypeAnnotations({
|
||||
"@TestConstructorParameterTypeAnnotations$MarkerTypeAnnotation(4)",
|
||||
"@TestConstructorParameterTypeAnnotations.MarkerTypeAnnotation(4)",
|
||||
"null"})
|
||||
public static class StaticNestedClass4 {
|
||||
public <P, Q> StaticNestedClass4(@MarkerTypeAnnotation(4) P parameter1,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -177,7 +177,7 @@ public class TestObjectMethods {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern annotationRegex = Pattern.compile("@TestObjectMethods\\$AnnotType\\((\\p{Digit})+\\)");
|
||||
private static final Pattern annotationRegex = Pattern.compile("@TestObjectMethods\\.AnnotType\\((\\p{Digit})+\\)");
|
||||
|
||||
static void testGetAnnotations(Class<?> clazz, boolean annotationsExpectedOnMethods) {
|
||||
System.err.println("Testing getAnnotations on methods of class " + clazz.getName());
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -162,7 +162,7 @@ public class RecordReflectionTest {
|
||||
RecordComponent rc = recordClass.getRecordComponents()[0];
|
||||
Annotation[] annos = rc.getAnnotations();
|
||||
assertEquals(annos.length, 1);
|
||||
assertEquals(annos[0].toString(), "@RecordReflectionTest$RCA()");
|
||||
assertEquals(annos[0].toString(), "@RecordReflectionTest.RCA()");
|
||||
|
||||
Field f = recordClass.getDeclaredField("i");
|
||||
assertEquals(f.getAnnotations().length, 1);
|
||||
@ -181,7 +181,7 @@ public class RecordReflectionTest {
|
||||
AnnotatedType at = rc.getAnnotatedType();
|
||||
Annotation[] annos = at.getAnnotations();
|
||||
assertEquals(annos.length, 1);
|
||||
assertEquals(annos[0].toString(), "@RecordReflectionTest$TYPE_USE()");
|
||||
assertEquals(annos[0].toString(), "@RecordReflectionTest.TYPE_USE()");
|
||||
|
||||
Field f = recordClass.getDeclaredField("i");
|
||||
assertEquals(f.getAnnotatedType().getAnnotations().length, 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user