Pass CompilerConfiguration to GenerationState instead of ton of parameters

This commit is contained in:
Alexander Udalov
2016-05-20 12:07:35 +03:00
parent 1492c71e88
commit f8816b5d67
13 changed files with 96 additions and 129 deletions
@@ -29,6 +29,8 @@ import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.inline.InlineCache
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
@@ -51,13 +53,8 @@ class GenerationState @JvmOverloads constructor(
val module: ModuleDescriptor,
bindingContext: BindingContext,
val files: List<KtFile>,
disableCallAssertions: Boolean = true,
disableParamAssertions: Boolean = true,
val configuration: CompilerConfiguration,
val generateDeclaredClassFilter: GenerateClassFilter = GenerationState.GenerateClassFilter.GENERATE_ALL,
disableInline: Boolean = false,
disableOptimization: Boolean = false,
val useTypeTableInSerializer: Boolean = false,
val inheritMultifileParts: Boolean = false,
val packagesWithObsoleteParts: Collection<FqName> = emptySet(),
val obsoleteMultifileClasses: Collection<FqName> = emptySet(),
// for PackageCodegen in incremental compilation mode
@@ -136,22 +133,18 @@ class GenerationState @JvmOverloads constructor(
var hasResult: Boolean = false
}
val isCallAssertionsEnabled: Boolean = !disableCallAssertions
@JvmName("isCallAssertionsEnabled") get
val isParamAssertionsEnabled: Boolean = !disableParamAssertions
@JvmName("isParamAssertionsEnabled") get
val isInlineEnabled: Boolean = !disableInline
@JvmName("isInlineEnabled") get
val isCallAssertionsEnabled: Boolean = !configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false)
val isParamAssertionsEnabled: Boolean = !configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false)
val isInlineEnabled: Boolean = !configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false)
val useTypeTableInSerializer: Boolean = configuration.get(JVMConfigurationKeys.USE_TYPE_TABLE, false)
val inheritMultifileParts: Boolean = configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
val rootContext: CodegenContext<*> = RootContext(this)
init {
this.interceptedBuilderFactory = builderFactory
.wrapWith(
{ OptimizationClassBuilderFactory(it, disableOptimization) },
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
{ BuilderFactoryForDuplicateSignatureDiagnostics(
it, this.bindingContext, diagnostics, fileClassesProvider,
getIncrementalCacheForThisTarget(),
@@ -397,13 +397,8 @@ object KotlinToJVMBytecodeCompiler {
result.moduleDescriptor,
result.bindingContext,
sourceFiles,
configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
configuration,
GenerationState.GenerateClassFilter.GENERATE_ALL,
configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false),
configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
/* useTypeTableInSerializer = */ false,
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false),
packagesWithObsoleteParts,
obsoleteMultifileClasses,
targetId,
@@ -411,7 +406,8 @@ object KotlinToJVMBytecodeCompiler {
outputDirectory,
incrementalCompilationComponents,
onIndependentPartCompilationEnd = onIndependentPartCompilationEnd,
dumpBinarySignatureMappingTo = configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH)?.let { File(it) })
dumpBinarySignatureMappingTo = configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH)?.let { File(it) }
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val generationStart = PerformanceCounter.currentTime()
@@ -70,7 +70,7 @@ import java.net.URLClassLoader
class ReplInterpreter(
disposable: Disposable,
configuration: CompilerConfiguration,
private val configuration: CompilerConfiguration,
private val ideMode: Boolean,
private val replReader: ReplSystemInWrapper?
) {
@@ -239,7 +239,9 @@ class ReplInterpreter(
val scriptDescriptor = doAnalyze(psiFile, errorHolder)
?: return LineResult.compileError(errorHolder.renderedDiagnostics)
val state = GenerationState(psiFile.project, ClassBuilderFactories.BINARIES, module, trace.bindingContext, listOf(psiFile))
val state = GenerationState(
psiFile.project, ClassBuilderFactories.BINARIES, module, trace.bindingContext, listOf(psiFile), configuration
)
compileScript(psiFile.script!!, earlierLines.map(EarlierLine::getScriptDescriptor), state, CompilationErrorHandler.THROW_EXCEPTION)
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.asJava
import com.google.common.collect.Lists
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
@@ -40,6 +39,7 @@ import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
import org.jetbrains.kotlin.fileClasses.getFileClassType
@@ -97,13 +97,10 @@ abstract class LightClassDataProvider<T : WithFileStubAndExtraDiagnostics>(
KotlinLightClassBuilderFactory(stubStack),
context.module,
context.bindingContext,
Lists.newArrayList(files),
disableCallAssertions = false,
disableParamAssertions = false,
generateDeclaredClassFilter = generateClassFilter,
disableInline = false,
disableOptimization = false,
useTypeTableInSerializer = false)
files.toMutableList(),
CompilerConfiguration.EMPTY,
generateClassFilter
)
state.beforeCompile()
bindingContext = state.bindingContext
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil
import org.jetbrains.kotlin.test.ConfigurationKind
@@ -33,7 +35,6 @@ abstract class AbstractDumpDeclarationsTest : CodegenTestCase() {
compileAndCompareDump(files, expectedResult)
}
private fun compileAndCompareDump(files: List<TestFile>, expectedResult: File) {
configurationKind = ConfigurationKind.NO_KOTLIN_REFLECT
@@ -50,19 +51,15 @@ abstract class AbstractDumpDeclarationsTest : CodegenTestCase() {
}
private fun compileManyFilesGetDeclarationsDump(files: List<KtFile>): File {
val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(files, myEnvironment)
analysisResult.throwIfError()
val (bindingContext, moduleDescriptor) =
JvmResolveUtil.analyzeAndCheckForErrors(files, myEnvironment).apply(AnalysisResult::throwIfError)
val dumpToFile = KotlinTestUtils.tmpDirForTest(this).resolve(this.name + ".json")
val state = GenerationState(
myEnvironment.project, ClassBuilderFactories.TEST,
analysisResult.moduleDescriptor, analysisResult.bindingContext,
files,
disableCallAssertions = false,
disableParamAssertions = false,
dumpBinarySignatureMappingTo = dumpToFile)
myEnvironment.project, ClassBuilderFactories.TEST, moduleDescriptor, bindingContext, files, CompilerConfiguration.EMPTY,
dumpBinarySignatureMappingTo = dumpToFile
)
KotlinCodegenFacade.compileCorrectFiles(state, org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
state.destroy()
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -57,13 +56,7 @@ public class CodegenTestUtil {
analysisResult.getModuleDescriptor(),
analysisResult.getBindingContext(),
files.getPsiFiles(),
configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
GenerationState.GenerateClassFilter.GENERATE_ALL,
configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false),
configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
/* useTypeTableInSerializer = */ false,
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false)
configuration
);
if (analysisResult.getShouldGenerateCode()) {
@@ -66,13 +66,8 @@ public class GenerationUtils {
CollectionsKt.first(files).getProject(), ClassBuilderFactories.TEST,
analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(),
files,
configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false),
configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false),
configuration,
GenerationState.GenerateClassFilter.GENERATE_ALL,
configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false),
configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false),
configuration.get(JVMConfigurationKeys.USE_TYPE_TABLE, false),
configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false),
Collections.<FqName>emptySet(),
Collections.<FqName>emptySet(),
null,
@@ -30,9 +30,11 @@ import com.intellij.util.containers.MultiMap
import org.apache.log4j.Logger
import org.jetbrains.annotations.TestOnly
import org.jetbrains.eval4j.Value
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
@@ -152,38 +154,30 @@ class KotlinDebuggerCaches(project: Project) {
}
}
private fun createTypeMapperForLibraryFile(element: KtElement, file: KtFile): KotlinTypeMapper {
return runReadAction {
val analysisResult = element.analyzeAndGetResult()
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
state.typeMapper
}
}
private fun getElementToCreateTypeMapperForLibraryFile(element: PsiElement?) =
runReadAction { if (element is KtElement) element else PsiTreeUtil.getParentOfType(element, KtElement::class.java)!! }
private fun createTypeMapperForSourceFile(file: KtFile): KotlinTypeMapper {
return runReadAction {
val analysisResult = file.analyzeFullyAndGetResult()
analysisResult.throwIfError()
private fun createTypeMapperForLibraryFile(element: KtElement, file: KtFile): KotlinTypeMapper =
runReadAction {
createTypeMapper(file, element.analyzeAndGetResult())
}
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file))
state.beforeCompile()
state.typeMapper
}
private fun createTypeMapperForSourceFile(file: KtFile): KotlinTypeMapper =
runReadAction {
createTypeMapper(file, file.analyzeFullyAndGetResult().apply(AnalysisResult::throwIfError))
}
private fun createTypeMapper(file: KtFile, analysisResult: AnalysisResult): KotlinTypeMapper {
val state = GenerationState(
file.project,
ClassBuilderFactories.THROW_EXCEPTION,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
listOf(file),
CompilerConfiguration.EMPTY
)
state.beforeCompile()
return state.typeMapper
}
@TestOnly fun addTypeMapper(file: KtFile, typeMapper: KotlinTypeMapper) {
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.Severity
@@ -394,7 +395,9 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
moduleDescriptor,
bindingContext,
files,
generateDeclaredClassFilter = generateClassFilter)
CompilerConfiguration.EMPTY,
generateClassFilter
)
val frameVisitor = FrameVisitor(context)
@@ -41,6 +41,8 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories;
import org.jetbrains.kotlin.codegen.CompilationErrorHandler;
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.config.JVMConfigurationKeys;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
@@ -73,7 +75,7 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
"No Kotlin source file is opened.\n" +
"*/";
public class UpdateBytecodeToolWindowTask extends LongRunningReadTask<Location, String> {
private class UpdateBytecodeToolWindowTask extends LongRunningReadTask<Location, String> {
@Override
protected Location prepareRequestInfo() {
if (!toolWindow.isVisible()) {
@@ -109,10 +111,22 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
@NotNull
@Override
protected String processRequest(@NotNull Location location) {
KtFile jetFile = location.getKFile();
assert jetFile != null;
KtFile ktFile = location.getKFile();
assert ktFile != null;
return getBytecodeForFile(jetFile, enableInline.isSelected(), enableAssertions.isSelected(), enableOptimization.isSelected());
CompilerConfiguration configuration = new CompilerConfiguration();
if (!enableInline.isSelected()) {
configuration.put(JVMConfigurationKeys.DISABLE_INLINE, true);
}
if (!enableAssertions.isSelected()) {
configuration.put(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, true);
configuration.put(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, true);
}
if (!enableOptimization.isSelected()) {
configuration.put(JVMConfigurationKeys.DISABLE_OPTIMIZATION, true);
}
return getBytecodeForFile(ktFile, configuration);
}
@Override
@@ -161,7 +175,7 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
private final JCheckBox enableAssertions;
private final JButton decompile;
public KotlinBytecodeToolWindow(final Project project, ToolWindow toolWindow) {
public KotlinBytecodeToolWindow(Project project, ToolWindow toolWindow) {
super(new BorderLayout());
myProject = project;
this.toolWindow = toolWindow;
@@ -214,15 +228,10 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
// public for tests
@NotNull
public static String getBytecodeForFile(
final KtFile jetFile,
boolean enableInline,
boolean enableAssertions,
boolean enableOptimization
) {
public static String getBytecodeForFile(@NotNull KtFile ktFile, @NotNull CompilerConfiguration configuration) {
GenerationState state;
try {
state = compileSingleFile(jetFile, enableInline, enableAssertions, enableOptimization);
state = compileSingleFile(ktFile, configuration);
}
catch (ProcessCanceledException e) {
throw e;
@@ -260,25 +269,19 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
}
@NotNull
public static GenerationState compileSingleFile(
final KtFile ktFile,
boolean enableInline,
boolean enableAssertions,
boolean enableOptimization
) {
public static GenerationState compileSingleFile(@NotNull final KtFile ktFile, @NotNull CompilerConfiguration configuration) {
ResolutionFacade resolutionFacade = ResolutionUtils.getResolutionFacade(ktFile);
BindingContext bindingContextForFile = resolutionFacade.analyzeFullyAndGetResult(Collections.singletonList(ktFile)).getBindingContext();
kotlin.Pair<BindingContext, List<KtFile>> result = DebuggerUtils.INSTANCE.analyzeInlinedFunctions(
resolutionFacade, bindingContextForFile, ktFile, !enableInline
resolutionFacade, bindingContextForFile, ktFile, configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false)
);
BindingContext bindingContext = result.getFirst();
List<KtFile> toProcess = result.getSecond();
GenerationState.GenerateClassFilter generateClassFilter = new GenerationState.GenerateClassFilter() {
@Override
public boolean shouldGeneratePackagePart(@NotNull KtFile file) {
return file == ktFile;
@@ -300,15 +303,10 @@ public class KotlinBytecodeToolWindow extends JPanel implements Disposable {
}
};
GenerationState state = new GenerationState(ktFile.getProject(), ClassBuilderFactories.TEST,
resolutionFacade.getModuleDescriptor(), bindingContext,
toProcess,
!enableAssertions,
!enableAssertions,
generateClassFilter,
!enableInline,
!enableOptimization,
/*useTypeTableInSerializer=*/false);
GenerationState state = new GenerationState(
ktFile.getProject(), ClassBuilderFactories.TEST, resolutionFacade.getModuleDescriptor(), bindingContext, toProcess,
configuration, generateClassFilter
);
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
return state;
}
@@ -25,6 +25,7 @@ import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
import org.jetbrains.java.decompiler.main.extern.IResultSaver
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.idea.actions.canBeDecompiledToJava
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
@@ -71,7 +72,7 @@ class KotlinDecompilerServiceImpl : KotlinDecompilerService {
}
fun bytecodeMapForSourceFile(file: KtFile): Map<File, () -> ByteArray> {
val generationState = KotlinBytecodeToolWindow.compileSingleFile(file, true, true, true)
val generationState = KotlinBytecodeToolWindow.compileSingleFile(file, CompilerConfiguration.EMPTY)
val bytecodeMap = hashMapOf<File, () -> ByteArray>()
generationState.factory.asList().filter { FileUtilRt.extensionEquals(it.relativePath, "class") }.forEach {
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.idea.internal
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
@@ -38,11 +40,13 @@ abstract class AbstractBytecodeToolWindowTest: KotlinLightCodeInsightFixtureTest
val file = myFixture.file as KtFile
val enableInline = InTextDirectivesUtils.getPrefixedBoolean(mainFileText, "// INLINE:") ?: true
val bytecodes = KotlinBytecodeToolWindow.getBytecodeForFile(file, enableInline, true, true)
val configuration = CompilerConfiguration()
if (InTextDirectivesUtils.getPrefixedBoolean(mainFileText, "// INLINE:") == false) {
configuration.put(JVMConfigurationKeys.DISABLE_INLINE, true)
}
val bytecodes = KotlinBytecodeToolWindow.getBytecodeForFile(file, configuration)
assert(bytecodes.contains("// ================")) {
"The header \"// ================\" is missing.\n This means that there is an exception failed during compilation:\n$bytecodes"
}
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
@@ -31,21 +32,14 @@ import org.jetbrains.org.objectweb.asm.ClassWriter
import java.io.File
class StubProducerExtension(val stubsOutputDir: File, val messageCollector: MessageCollector) : AnalysisCompletedHandlerExtension {
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingContext: BindingContext,
files: Collection<KtFile>
): AnalysisResult? {
val generationState = GenerationState(
project,
StubClassBuilderFactory(),
module,
bindingContext,
files.toList(),
disableCallAssertions = false,
disableParamAssertions = false)
val generationState =
GenerationState(project, StubClassBuilderFactory(), module, bindingContext, files.toList(), CompilerConfiguration.EMPTY)
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
@@ -74,4 +68,4 @@ private class StubClassBuilderFactory : ClassBuilderFactory {
override fun close() {
}
}
}