001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.load.kotlin;
018
019 import com.intellij.ide.highlighter.JavaClassFileType;
020 import com.intellij.openapi.Disposable;
021 import com.intellij.openapi.application.ApplicationManager;
022 import com.intellij.openapi.components.ServiceManager;
023 import com.intellij.openapi.util.Computable;
024 import com.intellij.openapi.vfs.VirtualFile;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027
028 public final class KotlinBinaryClassCache implements Disposable {
029 private static class RequestCache {
030 VirtualFile virtualFile;
031 long modificationStamp;
032 VirtualFileKotlinClass virtualFileKotlinClass;
033
034 public VirtualFileKotlinClass cache(VirtualFile file, VirtualFileKotlinClass aClass) {
035 virtualFile = file;
036 virtualFileKotlinClass = aClass;
037 modificationStamp = file.getModificationStamp();
038
039 return aClass;
040 }
041 }
042
043 private final ThreadLocal<RequestCache> cache =
044 new ThreadLocal<RequestCache>() {
045 @Override
046 protected RequestCache initialValue() {
047 return new RequestCache();
048 }
049 };
050
051 @Nullable
052 public static KotlinJvmBinaryClass getKotlinBinaryClass(@NotNull final VirtualFile file) {
053 if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
054
055 KotlinBinaryClassCache service = ServiceManager.getService(KotlinBinaryClassCache.class);
056 RequestCache requestCache = service.cache.get();
057
058 if (file.getModificationStamp() == requestCache.modificationStamp && file.equals(requestCache.virtualFile)) {
059 return requestCache.virtualFileKotlinClass;
060 }
061 else {
062 VirtualFileKotlinClass aClass = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFileKotlinClass>() {
063 @Override
064 public VirtualFileKotlinClass compute() {
065 //noinspection deprecation
066 return VirtualFileKotlinClass.Factory.create(file);
067 }
068 });
069
070 return requestCache.cache(file, aClass);
071 }
072 }
073
074 @Override
075 public void dispose() {
076 // This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is
077 // also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
078 // thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
079 cache.remove();
080 }
081 }