001 /*
002 * Copyright 2010-2014 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.jet.lang.resolve.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.vfs.VirtualFile;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026
027 public final class KotlinBinaryClassCache implements Disposable {
028 private static class RequestCache {
029 VirtualFile virtualFile;
030 long modificationStamp;
031 VirtualFileKotlinClass virtualFileKotlinClass;
032
033 public VirtualFileKotlinClass cache(VirtualFile file, VirtualFileKotlinClass aClass) {
034 virtualFile = file;
035 virtualFileKotlinClass = aClass;
036 modificationStamp = file.getModificationStamp();
037
038 return aClass;
039 }
040 }
041
042 private final ThreadLocal<RequestCache> cache =
043 new ThreadLocal<RequestCache>() {
044 @Override
045 protected RequestCache initialValue() {
046 return new RequestCache();
047 }
048 };
049
050 @Nullable
051 public static KotlinJvmBinaryClass getKotlinBinaryClass(@NotNull VirtualFile file) {
052 if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
053
054 KotlinBinaryClassCache service = ServiceManager.getService(KotlinBinaryClassCache.class);
055 RequestCache requestCache = service.cache.get();
056
057 if (file.getModificationStamp() == requestCache.modificationStamp && file.equals(requestCache.virtualFile)) {
058 return requestCache.virtualFileKotlinClass;
059 }
060 else {
061 ApplicationManager.getApplication().assertReadAccessAllowed();
062
063 //noinspection deprecation
064 VirtualFileKotlinClass aClass = VirtualFileKotlinClass.OBJECT$.create(file);
065 return requestCache.cache(file, aClass);
066 }
067 }
068
069 @Override
070 public void dispose() {
071 // 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
072 // also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
073 // thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
074 cache.remove();
075 }
076 }