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.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.OBJECT$.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 }