Deserialize annotations on package members
This commit is contained in:
@@ -129,7 +129,7 @@ public class NamespaceCodegen extends MemberCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeKotlinInfoIfNeeded() {
|
private void writeKotlinInfoIfNeeded() {
|
||||||
DescriptorSerializer serializer = new DescriptorSerializer();
|
DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(typeMapper));
|
||||||
ProtoBuf.Package.Builder packageProto = ProtoBuf.Package.newBuilder();
|
ProtoBuf.Package.Builder packageProto = ProtoBuf.Package.newBuilder();
|
||||||
boolean writeAnnotation = false;
|
boolean writeAnnotation = false;
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -36,9 +36,19 @@ public class JavaProtoBufUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static ClassData readJavaClassDataFrom(@NotNull byte[] data) {
|
private static ExtensionRegistryLite getExtensionRegistry() {
|
||||||
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
|
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
|
||||||
JavaProtoBuf.registerAllExtensions(registry);
|
JavaProtoBuf.registerAllExtensions(registry);
|
||||||
return ClassData.read(data, registry);
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static ClassData readClassDataFrom(@NotNull byte[] data) {
|
||||||
|
return ClassData.read(data, getExtensionRegistry());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static PackageData readPackageDataFrom(@NotNull byte[] data) {
|
||||||
|
return PackageData.read(data, getExtensionRegistry());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-6
@@ -26,10 +26,13 @@ import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
|
|||||||
import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
|
import org.jetbrains.jet.descriptors.serialization.descriptors.AnnotationDeserializer;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ClassOrNamespaceDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
|
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
|
||||||
import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
|
import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
|
||||||
|
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
|
||||||
import org.jetbrains.jet.lang.resolve.java.PsiClassFinder;
|
import org.jetbrains.jet.lang.resolve.java.PsiClassFinder;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
@@ -61,7 +64,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<AnnotationDescriptor> loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
|
public List<AnnotationDescriptor> loadClassAnnotations(@NotNull ClassDescriptor descriptor, @NotNull ProtoBuf.Class classProto) {
|
||||||
VirtualFile virtualFile = findVirtualFileByDescriptor(descriptor);
|
VirtualFile virtualFile = findVirtualFileByClass(descriptor);
|
||||||
try {
|
try {
|
||||||
return loadClassAnnotationsFromFile(virtualFile);
|
return loadClassAnnotationsFromFile(virtualFile);
|
||||||
}
|
}
|
||||||
@@ -71,7 +74,20 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private VirtualFile findVirtualFileByDescriptor(@NotNull ClassDescriptor descriptor) {
|
private VirtualFile findVirtualFileByDescriptor(@NotNull ClassOrNamespaceDescriptor descriptor) {
|
||||||
|
if (descriptor instanceof ClassDescriptor) {
|
||||||
|
return findVirtualFileByClass((ClassDescriptor) descriptor);
|
||||||
|
}
|
||||||
|
else if (descriptor instanceof NamespaceDescriptor) {
|
||||||
|
return findVirtualFileByPackage((NamespaceDescriptor) descriptor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException("Unrecognized descriptor: " + descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private VirtualFile findVirtualFileByClass(@NotNull ClassDescriptor descriptor) {
|
||||||
FqName fqName = kotlinFqNameToJavaFqName(naiveKotlinFqName(descriptor));
|
FqName fqName = kotlinFqNameToJavaFqName(naiveKotlinFqName(descriptor));
|
||||||
PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE /* TODO: ?! */);
|
PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE /* TODO: ?! */);
|
||||||
if (psiClass == null) {
|
if (psiClass == null) {
|
||||||
@@ -79,7 +95,21 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
|
|||||||
}
|
}
|
||||||
VirtualFile virtualFile = getVirtualFile(psiClass, fqName, (ClassOrNamespaceDescriptor) descriptor.getContainingDeclaration());
|
VirtualFile virtualFile = getVirtualFile(psiClass, fqName, (ClassOrNamespaceDescriptor) descriptor.getContainingDeclaration());
|
||||||
if (virtualFile == null) {
|
if (virtualFile == null) {
|
||||||
throw new IllegalStateException("Virtual file is not found for class: " + descriptor) ;
|
throw new IllegalStateException("Virtual file is not found for class: " + descriptor);
|
||||||
|
}
|
||||||
|
return virtualFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private VirtualFile findVirtualFileByPackage(@NotNull NamespaceDescriptor descriptor) {
|
||||||
|
FqName fqName = PackageClassUtils.getPackageClassFqName(DescriptorUtils.getFQName(descriptor).toSafe());
|
||||||
|
PsiClass psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE /* TODO: ?! */);
|
||||||
|
if (psiClass == null) {
|
||||||
|
throw new IllegalStateException("Psi class is not found for package: " + descriptor);
|
||||||
|
}
|
||||||
|
VirtualFile virtualFile = psiClass.getContainingFile().getVirtualFile();
|
||||||
|
if (virtualFile == null) {
|
||||||
|
throw new IllegalStateException("Virtual file is not found for package: " + descriptor);
|
||||||
}
|
}
|
||||||
return virtualFile;
|
return virtualFile;
|
||||||
}
|
}
|
||||||
@@ -147,9 +177,7 @@ public class AnnotationDescriptorDeserializer implements AnnotationDeserializer
|
|||||||
@NotNull ClassOrNamespaceDescriptor container,
|
@NotNull ClassOrNamespaceDescriptor container,
|
||||||
@NotNull ProtoBuf.Callable callableProto
|
@NotNull ProtoBuf.Callable callableProto
|
||||||
) {
|
) {
|
||||||
if (!(container instanceof ClassDescriptor)) return Collections.emptyList(); // TODO: NamespaceDescriptor
|
VirtualFile file = findVirtualFileByDescriptor(container);
|
||||||
|
|
||||||
VirtualFile file = findVirtualFileByDescriptor((ClassDescriptor) container);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: calculate this only once for each container
|
// TODO: calculate this only once for each container
|
||||||
|
|||||||
+2
-2
@@ -138,14 +138,14 @@ public final class DeserializedDescriptorResolver {
|
|||||||
private static ClassData readClassDataFromClassFile(@NotNull VirtualFile virtualFile) {
|
private static ClassData readClassDataFromClassFile(@NotNull VirtualFile virtualFile) {
|
||||||
byte[] data = getKotlinInfoDataFromClassFile(virtualFile);
|
byte[] data = getKotlinInfoDataFromClassFile(virtualFile);
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
return JavaProtoBufUtil.readJavaClassDataFrom(data);
|
return JavaProtoBufUtil.readClassDataFrom(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static PackageData readPackageDataFromClassFile(@NotNull VirtualFile virtualFile) {
|
private static PackageData readPackageDataFromClassFile(@NotNull VirtualFile virtualFile) {
|
||||||
byte[] data = getKotlinInfoDataFromClassFile(virtualFile);
|
byte[] data = getKotlinInfoDataFromClassFile(virtualFile);
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
return PackageData.read(data);
|
return JavaProtoBufUtil.readPackageDataFrom(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
+3
-2
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.descriptors.serialization;
|
package org.jetbrains.jet.descriptors.serialization;
|
||||||
|
|
||||||
|
import com.google.protobuf.ExtensionRegistryLite;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.utils.ExceptionUtils;
|
import org.jetbrains.jet.utils.ExceptionUtils;
|
||||||
|
|
||||||
@@ -24,11 +25,11 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public final class PackageData {
|
public final class PackageData {
|
||||||
@NotNull
|
@NotNull
|
||||||
public static PackageData read(@NotNull byte[] bytes) {
|
public static PackageData read(@NotNull byte[] bytes, @NotNull ExtensionRegistryLite registry) {
|
||||||
try {
|
try {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
|
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
|
||||||
NameResolver nameResolver = NameSerializationUtil.deserializeNameResolver(in);
|
NameResolver nameResolver = NameSerializationUtil.deserializeNameResolver(in);
|
||||||
ProtoBuf.Package packageProto = ProtoBuf.Package.parseFrom(in);
|
ProtoBuf.Package packageProto = ProtoBuf.Package.parseFrom(in, registry);
|
||||||
return new PackageData(nameResolver, packageProto);
|
return new PackageData(nameResolver, packageProto);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
annotation class Anno
|
||||||
|
|
||||||
|
Anno fun function() {}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
test.Anno() internal fun function(): jet.Unit
|
||||||
|
|
||||||
|
internal final annotation class Anno : jet.Annotation {
|
||||||
|
/*primary*/ public constructor Anno()
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.google.protobuf.ExtensionRegistryLite.getEmptyRegistry;
|
||||||
|
|
||||||
public class KotlinInfoForPackageTest extends CodegenTestCase {
|
public class KotlinInfoForPackageTest extends CodegenTestCase {
|
||||||
public static final FqName NAMESPACE_NAME = new FqName("test");
|
public static final FqName NAMESPACE_NAME = new FqName("test");
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ public class KotlinInfoForPackageTest extends CodegenTestCase {
|
|||||||
assertTrue(aClass.isAnnotationPresent(KotlinInfo.class));
|
assertTrue(aClass.isAnnotationPresent(KotlinInfo.class));
|
||||||
KotlinInfo kotlinInfo = (KotlinInfo) aClass.getAnnotation(KotlinInfo.class);
|
KotlinInfo kotlinInfo = (KotlinInfo) aClass.getAnnotation(KotlinInfo.class);
|
||||||
|
|
||||||
PackageData data = PackageData.read(kotlinInfo.data());
|
PackageData data = PackageData.read(kotlinInfo.data(), getEmptyRegistry());
|
||||||
|
|
||||||
Set<String> classNames = collectClassNames(data);
|
Set<String> classNames = collectClassNames(data);
|
||||||
assertSameElements(Arrays.asList("A", "B", "C"), classNames);
|
assertSameElements(Arrays.asList("A", "B", "C"), classNames);
|
||||||
|
|||||||
+15
-1
@@ -38,7 +38,7 @@ public class DescriptorSerializationTestGenerated extends AbstractDescriptorSeri
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
||||||
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class})
|
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class, Annotations.PackageMembers.class})
|
||||||
public static class Annotations extends AbstractDescriptorSerializationTest {
|
public static class Annotations extends AbstractDescriptorSerializationTest {
|
||||||
public void testAllFilesPresentInAnnotations() throws Exception {
|
public void testAllFilesPresentInAnnotations() throws Exception {
|
||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
@@ -105,11 +105,25 @@ public class DescriptorSerializationTestGenerated extends AbstractDescriptorSeri
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/loadKotlin/annotations/packageMembers")
|
||||||
|
public static class PackageMembers extends AbstractDescriptorSerializationTest {
|
||||||
|
public void testAllFilesPresentInPackageMembers() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations/packageMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Function.kt")
|
||||||
|
public void testFunction() throws Exception {
|
||||||
|
doTest("compiler/testData/loadKotlin/annotations/packageMembers/Function.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static Test innerSuite() {
|
public static Test innerSuite() {
|
||||||
TestSuite suite = new TestSuite("Annotations");
|
TestSuite suite = new TestSuite("Annotations");
|
||||||
suite.addTestSuite(Annotations.class);
|
suite.addTestSuite(Annotations.class);
|
||||||
suite.addTestSuite(ClassMembers.class);
|
suite.addTestSuite(ClassMembers.class);
|
||||||
suite.addTestSuite(Classes.class);
|
suite.addTestSuite(Classes.class);
|
||||||
|
suite.addTestSuite(PackageMembers.class);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
||||||
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class})
|
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class, Annotations.PackageMembers.class})
|
||||||
public static class Annotations extends AbstractLoadCompiledKotlinTest {
|
public static class Annotations extends AbstractLoadCompiledKotlinTest {
|
||||||
public void testAllFilesPresentInAnnotations() throws Exception {
|
public void testAllFilesPresentInAnnotations() throws Exception {
|
||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
@@ -105,11 +105,25 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/loadKotlin/annotations/packageMembers")
|
||||||
|
public static class PackageMembers extends AbstractLoadCompiledKotlinTest {
|
||||||
|
public void testAllFilesPresentInPackageMembers() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations/packageMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Function.kt")
|
||||||
|
public void testFunction() throws Exception {
|
||||||
|
doTestWithAccessors("compiler/testData/loadKotlin/annotations/packageMembers/Function.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static Test innerSuite() {
|
public static Test innerSuite() {
|
||||||
TestSuite suite = new TestSuite("Annotations");
|
TestSuite suite = new TestSuite("Annotations");
|
||||||
suite.addTestSuite(Annotations.class);
|
suite.addTestSuite(Annotations.class);
|
||||||
suite.addTestSuite(ClassMembers.class);
|
suite.addTestSuite(ClassMembers.class);
|
||||||
suite.addTestSuite(Classes.class);
|
suite.addTestSuite(Classes.class);
|
||||||
|
suite.addTestSuite(PackageMembers.class);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -40,7 +40,7 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
@TestMetadata("compiler/testData/loadKotlin/annotations")
|
||||||
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class})
|
@InnerTestClasses({Annotations.ClassMembers.class, Annotations.Classes.class, Annotations.PackageMembers.class})
|
||||||
public static class Annotations extends AbstractLazyResolveNamespaceComparingTest {
|
public static class Annotations extends AbstractLazyResolveNamespaceComparingTest {
|
||||||
public void testAllFilesPresentInAnnotations() throws Exception {
|
public void testAllFilesPresentInAnnotations() throws Exception {
|
||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
@@ -107,11 +107,25 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/loadKotlin/annotations/packageMembers")
|
||||||
|
public static class PackageMembers extends AbstractLazyResolveNamespaceComparingTest {
|
||||||
|
public void testAllFilesPresentInPackageMembers() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/annotations/packageMembers"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Function.kt")
|
||||||
|
public void testFunction() throws Exception {
|
||||||
|
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/annotations/packageMembers/Function.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static Test innerSuite() {
|
public static Test innerSuite() {
|
||||||
TestSuite suite = new TestSuite("Annotations");
|
TestSuite suite = new TestSuite("Annotations");
|
||||||
suite.addTestSuite(Annotations.class);
|
suite.addTestSuite(Annotations.class);
|
||||||
suite.addTestSuite(ClassMembers.class);
|
suite.addTestSuite(ClassMembers.class);
|
||||||
suite.addTestSuite(Classes.class);
|
suite.addTestSuite(Classes.class);
|
||||||
|
suite.addTestSuite(PackageMembers.class);
|
||||||
return suite;
|
return suite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user