Add coroutines-compat jar dependency to those tests that require it
#KT-36083
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
// !LANGUAGE: -ExtendedMainConvention -ReleaseCoroutines
|
// !LANGUAGE: -ExtendedMainConvention -ReleaseCoroutines
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
// uses kotlin.coroutines.experimental classes under the hood
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {}
|
suspend fun main(args: Array<String>) {}
|
||||||
|
|||||||
+5
-4
@@ -109,13 +109,14 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
|
|||||||
val oldModule = modules[testModule]!!
|
val oldModule = modules[testModule]!!
|
||||||
|
|
||||||
val languageVersionSettings =
|
val languageVersionSettings =
|
||||||
if (coroutinesPackage.isNotEmpty())
|
if (coroutinesPackage.isNotEmpty()) {
|
||||||
|
val isExperimental = coroutinesPackage == DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString()
|
||||||
CompilerTestLanguageVersionSettings(
|
CompilerTestLanguageVersionSettings(
|
||||||
DEFAULT_DIAGNOSTIC_TESTS_FEATURES,
|
DEFAULT_DIAGNOSTIC_TESTS_FEATURES,
|
||||||
if (coroutinesPackage.contains("experimental")) ApiVersion.KOTLIN_1_2 else ApiVersion.KOTLIN_1_3,
|
if (isExperimental) ApiVersion.KOTLIN_1_2 else ApiVersion.KOTLIN_1_3,
|
||||||
if (coroutinesPackage.contains("experimental")) LanguageVersion.KOTLIN_1_2 else LanguageVersion.KOTLIN_1_3
|
if (isExperimental) LanguageVersion.KOTLIN_1_2 else LanguageVersion.KOTLIN_1_3
|
||||||
)
|
)
|
||||||
else loadLanguageVersionSettings(testFilesInModule)
|
} else loadLanguageVersionSettings(testFilesInModule)
|
||||||
|
|
||||||
languageVersionSettingsByModule[testModule] = languageVersionSettings
|
languageVersionSettingsByModule[testModule] = languageVersionSettings
|
||||||
|
|
||||||
|
|||||||
+6
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
|||||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
|
||||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition;
|
import org.jetbrains.kotlin.parsing.KotlinParserDefinition;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.test.*;
|
import org.jetbrains.kotlin.test.*;
|
||||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||||
|
|
||||||
@@ -114,6 +115,11 @@ public abstract class KotlinMultiFileTestWithJava<M, F> extends KtUsefulTestCase
|
|||||||
result.add(ForTestCompileRuntime.runtimeJarForTestsWithJdk8());
|
result.add(ForTestCompileRuntime.runtimeJarForTestsWithJdk8());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString().equals(coroutinesPackage) ||
|
||||||
|
fileText.contains(DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString())) {
|
||||||
|
result.add(ForTestCompileRuntime.coroutinesCompatForTests());
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
|||||||
import org.jetbrains.kotlin.codegen.ir.AbstractFirBlackBoxCodegenTest;
|
import org.jetbrains.kotlin.codegen.ir.AbstractFirBlackBoxCodegenTest;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
|
|||||||
if (!InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "CHECK_BYTECODE_LISTING")) return;
|
if (!InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "CHECK_BYTECODE_LISTING")) return;
|
||||||
|
|
||||||
String suffix =
|
String suffix =
|
||||||
(coroutinesPackage.contains("experimental") || coroutinesPackage.isEmpty())
|
(coroutinesPackage.equals(DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString()) || coroutinesPackage.isEmpty())
|
||||||
&& InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "COMMON_COROUTINES_TEST")
|
&& InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "COMMON_COROUTINES_TEST")
|
||||||
? "_1_2" :
|
? "_1_2" :
|
||||||
getBackend().isIR() ? "_ir" : "";
|
getBackend().isIR() ? "_ir" : "";
|
||||||
|
|||||||
+4
-1
@@ -86,7 +86,10 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTest
|
|||||||
@NotNull
|
@NotNull
|
||||||
private URLClassLoader createGeneratedClassLoader() throws Exception {
|
private URLClassLoader createGeneratedClassLoader() throws Exception {
|
||||||
return new URLClassLoader(
|
return new URLClassLoader(
|
||||||
new URL[]{ bDir.toURI().toURL(), aDir.toURI().toURL() },
|
new URL[]{
|
||||||
|
bDir.toURI().toURL(), aDir.toURI().toURL(),
|
||||||
|
ForTestCompileRuntime.coroutinesCompatForTests().toURI().toURL()
|
||||||
|
},
|
||||||
ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
|
ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,15 @@ import org.jetbrains.kotlin.cli.common.output.OutputUtilsKt;
|
|||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace;
|
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace;
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot;
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt;
|
||||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.config.*;
|
import org.jetbrains.kotlin.config.*;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider;
|
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider;
|
||||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper;
|
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper;
|
||||||
import org.jetbrains.kotlin.test.*;
|
import org.jetbrains.kotlin.test.*;
|
||||||
@@ -59,10 +62,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -166,6 +166,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
) {
|
) {
|
||||||
LanguageVersionSettings explicitLanguageVersionSettings = null;
|
LanguageVersionSettings explicitLanguageVersionSettings = null;
|
||||||
boolean disableReleaseCoroutines = false;
|
boolean disableReleaseCoroutines = false;
|
||||||
|
boolean includeCompatExperimentalCoroutines = false;
|
||||||
|
|
||||||
List<String> kotlinConfigurationFlags = new ArrayList<>(0);
|
List<String> kotlinConfigurationFlags = new ArrayList<>(0);
|
||||||
for (TestFile testFile : testFilesWithConfigurationDirectives) {
|
for (TestFile testFile : testFilesWithConfigurationDirectives) {
|
||||||
@@ -191,11 +192,16 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
|
|
||||||
if (!InTextDirectivesUtils.findLinesWithPrefixesRemoved(testFile.content, "// COMMON_COROUTINES_TEST").isEmpty()) {
|
if (!InTextDirectivesUtils.findLinesWithPrefixesRemoved(testFile.content, "// COMMON_COROUTINES_TEST").isEmpty()) {
|
||||||
assert !testFile.content.contains("COROUTINES_PACKAGE") : "Must replace COROUTINES_PACKAGE prior to tests compilation";
|
assert !testFile.content.contains("COROUTINES_PACKAGE") : "Must replace COROUTINES_PACKAGE prior to tests compilation";
|
||||||
if (coroutinesPackage.equals("kotlin.coroutines.experimental")) {
|
if (DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString().equals(coroutinesPackage)) {
|
||||||
disableReleaseCoroutines = true;
|
disableReleaseCoroutines = true;
|
||||||
|
includeCompatExperimentalCoroutines = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (testFile.content.contains(DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString())) {
|
||||||
|
includeCompatExperimentalCoroutines = true;
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, String> directives = KotlinTestUtils.parseDirectives(testFile.content);
|
Map<String, String> directives = KotlinTestUtils.parseDirectives(testFile.content);
|
||||||
|
|
||||||
LanguageVersionSettings fileLanguageVersionSettings = parseLanguageVersionSettings(directives);
|
LanguageVersionSettings fileLanguageVersionSettings = parseLanguageVersionSettings(directives);
|
||||||
@@ -213,6 +219,9 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
Collections.emptyMap()
|
Collections.emptyMap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (includeCompatExperimentalCoroutines) {
|
||||||
|
JvmContentRootsKt.addJvmClasspathRoot(configuration, ForTestCompileRuntime.coroutinesCompatForTests());
|
||||||
|
}
|
||||||
|
|
||||||
if (explicitLanguageVersionSettings != null) {
|
if (explicitLanguageVersionSettings != null) {
|
||||||
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, explicitLanguageVersionSettings);
|
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, explicitLanguageVersionSettings);
|
||||||
@@ -425,6 +434,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
if (additionalDependencies != null) {
|
if (additionalDependencies != null) {
|
||||||
files.addAll(additionalDependencies);
|
files.addAll(additionalDependencies);
|
||||||
}
|
}
|
||||||
|
files.addAll(getExtraDependenciesFromKotlinCompileClasspath());
|
||||||
|
|
||||||
ScriptDependenciesProvider externalImportsProvider =
|
ScriptDependenciesProvider externalImportsProvider =
|
||||||
ScriptDependenciesProvider.Companion.getInstance(myEnvironment.getProject());
|
ScriptDependenciesProvider.Companion.getInstance(myEnvironment.getProject());
|
||||||
@@ -451,6 +461,20 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<File> getExtraDependenciesFromKotlinCompileClasspath() {
|
||||||
|
List<File> includeFromCompileClasspath = CollectionsKt.listOf(
|
||||||
|
ForTestCompileRuntime.coroutinesCompatForTests()
|
||||||
|
);
|
||||||
|
List<File> compileClasspath =
|
||||||
|
CollectionsKt.map(
|
||||||
|
CollectionsKt.filterIsInstance(
|
||||||
|
myEnvironment.getConfiguration().get(CLIConfigurationKeys.CONTENT_ROOTS),
|
||||||
|
JvmClasspathRoot.class),
|
||||||
|
JvmClasspathRoot::getFile);
|
||||||
|
return CollectionsKt.intersect(compileClasspath, includeFromCompileClasspath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected String generateToText() {
|
protected String generateToText() {
|
||||||
return generateToText(null);
|
return generateToText(null);
|
||||||
@@ -699,6 +723,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
|
|||||||
if (loadAndroidAnnotations) {
|
if (loadAndroidAnnotations) {
|
||||||
javaClasspath.add(ForTestCompileRuntime.androidAnnotationsForTests().getPath());
|
javaClasspath.add(ForTestCompileRuntime.androidAnnotationsForTests().getPath());
|
||||||
}
|
}
|
||||||
|
javaClasspath.addAll(CollectionsKt.map(getExtraDependenciesFromKotlinCompileClasspath(), File::getPath));
|
||||||
|
|
||||||
javaClassesOutputDirectory = getJavaClassesOutputDirectory();
|
javaClassesOutputDirectory = getJavaClassesOutputDirectory();
|
||||||
compileJava(findJavaSourcesInDirectory(javaSourceDir), javaClasspath, javacOptions, javaClassesOutputDirectory);
|
compileJava(findJavaSourcesInDirectory(javaSourceDir), javaClasspath, javacOptions, javaClassesOutputDirectory);
|
||||||
|
|||||||
+5
@@ -100,6 +100,11 @@ public class ForTestCompileRuntime {
|
|||||||
return assertExists(new File("dist/kotlinc/lib/kotlin-annotations-android.jar"));
|
return assertExists(new File("dist/kotlinc/lib/kotlin-annotations-android.jar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static File coroutinesCompatForTests() {
|
||||||
|
return assertExists(new File("dist/kotlinc/lib/kotlin-coroutines-experimental-compat.jar"));
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static File assertExists(@NotNull File file) {
|
private static File assertExists(@NotNull File file) {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
|
|||||||
+2
-2
@@ -59,7 +59,7 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
|||||||
},
|
},
|
||||||
checkKotlinOutput: (String) -> Unit = { actual -> assertEquals(normalizeOutput("" to ExitCode.OK), actual) },
|
checkKotlinOutput: (String) -> Unit = { actual -> assertEquals(normalizeOutput("" to ExitCode.OK), actual) },
|
||||||
manifest: Manifest? = null,
|
manifest: Manifest? = null,
|
||||||
vararg extraClassPath: File
|
extraClassPath: List<File> = emptyList()
|
||||||
): File {
|
): File {
|
||||||
val sourceDir = File(testDataDirectory, libraryName)
|
val sourceDir = File(testDataDirectory, libraryName)
|
||||||
val javaFiles = FileUtil.findFilesByMask(JAVA_FILES, sourceDir)
|
val javaFiles = FileUtil.findFilesByMask(JAVA_FILES, sourceDir)
|
||||||
@@ -70,7 +70,7 @@ abstract class AbstractKotlinCompilerIntegrationTest : TestCaseWithTmpdir() {
|
|||||||
|
|
||||||
val outputDir = if (isJar) File(tmpdir, "output-$libraryName") else destination
|
val outputDir = if (isJar) File(tmpdir, "output-$libraryName") else destination
|
||||||
if (kotlinFiles.isNotEmpty()) {
|
if (kotlinFiles.isNotEmpty()) {
|
||||||
val output = compileKotlin(libraryName, outputDir, extraClassPath.toList(), K2JVMCompiler(), additionalOptions, expectedFileName = null)
|
val output = compileKotlin(libraryName, outputDir, extraClassPath, K2JVMCompiler(), additionalOptions, expectedFileName = null)
|
||||||
checkKotlinOutput(normalizeOutput(output))
|
checkKotlinOutput(normalizeOutput(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -438,7 +438,7 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
|||||||
|
|
||||||
fun testReplaceAnnotationClassWithInterface() {
|
fun testReplaceAnnotationClassWithInterface() {
|
||||||
val library1 = compileLibrary("library-1")
|
val library1 = compileLibrary("library-1")
|
||||||
val usage = compileLibrary("usage", extraClassPath = *arrayOf(library1))
|
val usage = compileLibrary("usage", extraClassPath = listOf(library1))
|
||||||
val library2 = compileLibrary("library-2")
|
val library2 = compileLibrary("library-2")
|
||||||
doTestWithTxt(usage, library2)
|
doTestWithTxt(usage, library2)
|
||||||
}
|
}
|
||||||
@@ -507,7 +507,7 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
|||||||
val version = intArrayOf(1, 0, 1) // legacy coroutines metadata
|
val version = intArrayOf(1, 0, 1) // legacy coroutines metadata
|
||||||
val options = listOf("-language-version", "1.2", "-Xcoroutines=enable")
|
val options = listOf("-language-version", "1.2", "-Xcoroutines=enable")
|
||||||
val library = transformJar(
|
val library = transformJar(
|
||||||
compileLibrary("library", additionalOptions = options),
|
compileLibrary("library", additionalOptions = options, extraClassPath = listOf(ForTestCompileRuntime.coroutinesCompatForTests())),
|
||||||
{ _, bytes ->
|
{ _, bytes ->
|
||||||
val (resultBytes, removedCounter) = stripSuspensionMarksToImitateLegacyCompiler(
|
val (resultBytes, removedCounter) = stripSuspensionMarksToImitateLegacyCompiler(
|
||||||
WrongBytecodeVersionTest.transformMetadataInClassFile(bytes) { name, _ ->
|
WrongBytecodeVersionTest.transformMetadataInClassFile(bytes) { name, _ ->
|
||||||
@@ -518,11 +518,11 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
|||||||
resultBytes
|
resultBytes
|
||||||
})
|
})
|
||||||
compileKotlin(
|
compileKotlin(
|
||||||
"source.kt", tmpdir, listOf(library), K2JVMCompiler(),
|
"source.kt", tmpdir, listOf(library, ForTestCompileRuntime.coroutinesCompatForTests()), K2JVMCompiler(),
|
||||||
additionalOptions = options
|
additionalOptions = options
|
||||||
)
|
)
|
||||||
val classLoader = URLClassLoader(
|
val classLoader = URLClassLoader(
|
||||||
arrayOf(library.toURI().toURL(), tmpdir.toURI().toURL()),
|
arrayOf(library.toURI().toURL(), tmpdir.toURI().toURL(), ForTestCompileRuntime.coroutinesCompatForTests().toURI().toURL()),
|
||||||
ForTestCompileRuntime.runtimeJarClassLoader()
|
ForTestCompileRuntime.runtimeJarClassLoader()
|
||||||
)
|
)
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -576,12 +576,13 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
|
|||||||
val library = compileLibrary(
|
val library = compileLibrary(
|
||||||
"library",
|
"library",
|
||||||
additionalOptions = listOf("-language-version", "1.2"),
|
additionalOptions = listOf("-language-version", "1.2"),
|
||||||
checkKotlinOutput = {}
|
checkKotlinOutput = {},
|
||||||
|
extraClassPath = listOf(ForTestCompileRuntime.coroutinesCompatForTests())
|
||||||
)
|
)
|
||||||
compileKotlin(
|
compileKotlin(
|
||||||
"release.kt",
|
"release.kt",
|
||||||
tmpdir,
|
tmpdir,
|
||||||
listOf(library),
|
listOf(library, ForTestCompileRuntime.coroutinesCompatForTests()),
|
||||||
additionalOptions = listOf("-language-version", "1.3", "-api-version", "1.3")
|
additionalOptions = listOf("-language-version", "1.3", "-api-version", "1.3")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user