2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.compiler
.make
;
18 import com
.intellij
.compiler
.SymbolTable
;
19 import com
.intellij
.compiler
.classParsing
.*;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.openapi
.util
.io
.FileUtil
;
22 import com
.intellij
.util
.ArrayUtil
;
23 import com
.intellij
.util
.cls
.ClsFormatException
;
24 import com
.intellij
.util
.io
.DataExternalizer
;
25 import com
.intellij
.util
.io
.EnumeratorIntegerDescriptor
;
26 import com
.intellij
.util
.io
.PersistentHashMap
;
27 import org
.jetbrains
.annotations
.NonNls
;
28 import org
.jetbrains
.annotations
.Nullable
;
30 import java
.io
.DataInput
;
31 import java
.io
.DataOutput
;
33 import java
.io
.IOException
;
37 * @author Eugene Zhuravlev
42 private static final Logger LOG
= Logger
.getInstance("#com.intellij.compiler.make.Cache");
43 public static final int UNKNOWN
= -1;
45 private final PersistentHashMap
<Integer
, ClassInfo
> myQNameToClassInfoMap
;
47 private final BackwardDependenciesStorage myDependencies
;
48 private final CompilerDependencyStorage
<Integer
> myQNameToReferencedClassesMap
;
49 private final CompilerDependencyStorage
<Integer
> myQNameToSubclassesMap
;
50 private final PersistentHashMap
<Integer
, Boolean
> myRemoteQNames
;
51 private final String myStorePath
;
53 public Cache(@NonNls final String storePath
, final int cacheSize
) throws IOException
{
54 myStorePath
= storePath
;
55 new File(storePath
).mkdirs();
56 myQNameToClassInfoMap
= new CachedPersistentHashMap
<Integer
, ClassInfo
>(getOrCreateFile("classes"), EnumeratorIntegerDescriptor
.INSTANCE
, new DataExternalizer
<ClassInfo
>() {
57 public void save(DataOutput out
, ClassInfo value
) throws IOException
{
60 public ClassInfo
read(DataInput in
) throws IOException
{
61 return new ClassInfo(in
);
64 protected boolean isValueDirty(ClassInfo classInfo
) {
65 return classInfo
.isDirty();
69 myDependencies
= new BackwardDependenciesStorage(getOrCreateFile("bdeps"), cacheSize
);
70 myQNameToReferencedClassesMap
= new CompilerDependencyStorage
<Integer
>(getOrCreateFile("fdeps"), EnumeratorIntegerDescriptor
.INSTANCE
, cacheSize
);
71 myQNameToSubclassesMap
= new CompilerDependencyStorage
<Integer
>(getOrCreateFile("subclasses"), EnumeratorIntegerDescriptor
.INSTANCE
, cacheSize
);
73 myRemoteQNames
= new PersistentHashMap
<Integer
, Boolean
>(getOrCreateFile("remote"), EnumeratorIntegerDescriptor
.INSTANCE
, new DataExternalizer
<Boolean
>() {
74 public void save(DataOutput out
, Boolean value
) throws IOException
{
75 out
.writeBoolean(value
.booleanValue());
78 public Boolean
read(DataInput in
) throws IOException
{
79 return in
.readBoolean();
84 private File
getOrCreateFile(final String fileName
) throws IOException
{
85 final File file
= new File(myStorePath
, fileName
);
86 FileUtil
.createIfDoesntExist(file
);
90 public void dispose() throws CacheCorruptedException
{
91 CacheCorruptedException ex
= null;
93 myQNameToClassInfoMap
.close();
95 catch (IOException e
) {
97 ex
= new CacheCorruptedException(e
);
100 myRemoteQNames
.close();
102 catch (IOException e
) {
105 ex
= new CacheCorruptedException(e
);
109 myQNameToReferencedClassesMap
.dispose();
110 myDependencies
.dispose();
111 myQNameToSubclassesMap
.dispose();
119 public int[] getAllClassNames() throws CacheCorruptedException
{
121 final Collection
<Integer
> allKeys
= myQNameToClassInfoMap
.getAllKeysWithExistingMapping();
122 final int[] array
= ArrayUtil
.newIntArray(allKeys
.size());
124 for (Integer id
: allKeys
) {
125 array
[idx
++] = id
.intValue();
129 catch (IOException e
) {
130 throw new CacheCorruptedException(e
);
134 public int importClassInfo(ClassFileReader reader
, SymbolTable symbolTable
) throws ClsFormatException
, CacheCorruptedException
{
136 final ClassInfo classInfo
= new ClassInfo(reader
, symbolTable
);
137 myQNameToClassInfoMap
.put(classInfo
.getQualifiedName(), classInfo
);
138 return classInfo
.getQualifiedName();
140 catch (IOException e
) {
141 throw new CacheCorruptedException(e
);
145 public void importClassInfo(Cache fromCache
, final int qName
) throws CacheCorruptedException
{
147 final ClassInfo classInfo
= fromCache
.myQNameToClassInfoMap
.get(qName
);
148 if (classInfo
!= null) {
149 final ClassInfo clone
= classInfo
.clone();
150 clone
.clearReferences();
151 myQNameToClassInfoMap
.put(qName
, clone
);
154 catch (Throwable e
) {
155 throw new CacheCorruptedException(e
);
159 public AnnotationConstantValue
[] getRuntimeVisibleAnnotations(int classId
) throws CacheCorruptedException
{
161 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
162 return classInfo
!= null? classInfo
.getRuntimeVisibleAnnotations() : AnnotationConstantValue
.EMPTY_ARRAY
;
164 catch (Throwable e
) {
165 throw new CacheCorruptedException(e
);
169 public AnnotationConstantValue
[] getRuntimeInvisibleAnnotations(int classId
) throws CacheCorruptedException
{
171 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
172 return classInfo
!= null? classInfo
.getRuntimeInvisibleAnnotations() : AnnotationConstantValue
.EMPTY_ARRAY
;
174 catch (Throwable e
) {
175 throw new CacheCorruptedException(e
);
179 public int[] getSubclasses(int classId
) throws CacheCorruptedException
{
181 return myQNameToSubclassesMap
.getValues(classId
);
183 catch (Throwable e
) {
184 throw new CacheCorruptedException(e
);
188 public void addSubclass(int classId
, int subclassQName
) throws CacheCorruptedException
{
190 myQNameToSubclassesMap
.addValue(classId
, subclassQName
);
192 catch (Throwable e
) {
193 throw new CacheCorruptedException(e
);
197 public void removeSubclass(int classId
, int subclassQName
) throws CacheCorruptedException
{
199 myQNameToSubclassesMap
.removeValue(classId
, subclassQName
);
201 catch (Throwable e
) {
202 throw new CacheCorruptedException(e
);
206 public int[] getReferencedClasses(int classId
) throws CacheCorruptedException
{
208 return myQNameToReferencedClassesMap
.getValues(classId
);
210 catch (Throwable e
) {
211 throw new CacheCorruptedException(e
);
215 public void clearReferencedClasses(int qName
) throws CacheCorruptedException
{
217 myQNameToReferencedClassesMap
.remove(qName
);
219 catch (Throwable e
) {
220 throw new CacheCorruptedException(e
);
224 public Collection
<ReferenceInfo
> getReferences(int classId
) throws CacheCorruptedException
{
226 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
227 return classInfo
!= null? Arrays
.asList(classInfo
.getReferences()) : Collections
.<ReferenceInfo
>emptyList();
229 catch (Throwable e
) {
230 throw new CacheCorruptedException(e
);
234 public String
getSourceFileName(int classId
) throws CacheCorruptedException
{
236 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
237 return classInfo
!= null? classInfo
.getSourceFileName() : "";
239 catch (Throwable e
) {
240 throw new CacheCorruptedException(e
);
244 public boolean isRemote(int classId
) throws CacheCorruptedException
{
246 return myRemoteQNames
.containsMapping(classId
);
248 catch (Throwable e
) {
249 throw new CacheCorruptedException(e
);
253 public void setRemote(int classId
, boolean remote
) throws CacheCorruptedException
{
256 myRemoteQNames
.put(classId
, Boolean
.TRUE
);
259 myRemoteQNames
.remove(classId
);
262 catch (Throwable e
) {
263 throw new CacheCorruptedException(e
);
267 public int getSuperQualifiedName(int classId
) throws CacheCorruptedException
{
269 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
270 return classInfo
!= null? classInfo
.getSuperQualifiedName() : UNKNOWN
;
272 catch (Throwable e
) {
273 throw new CacheCorruptedException(e
);
277 public String
getPath(int classId
) throws CacheCorruptedException
{
279 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
280 return classInfo
!= null? classInfo
.getPath() : "";
282 catch (Throwable e
) {
283 throw new CacheCorruptedException(e
);
287 public void setPath(int classId
, String path
) throws CacheCorruptedException
{
289 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
290 if (classInfo
!= null) {
291 classInfo
.setPath(path
);
294 catch (Throwable e
) {
295 throw new CacheCorruptedException(e
);
299 public int getGenericSignature(int classId
) throws CacheCorruptedException
{
301 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
302 return classInfo
!= null? classInfo
.getGenericSignature() : UNKNOWN
;
304 catch (Throwable e
) {
305 throw new CacheCorruptedException(e
);
309 public boolean containsClass(int qName
) throws CacheCorruptedException
{
311 return myQNameToClassInfoMap
.containsMapping(qName
);
313 catch (IOException e
) {
314 throw new CacheCorruptedException(e
);
318 public int[] getSuperInterfaces(int classId
) throws CacheCorruptedException
{
320 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
321 return classInfo
!= null? classInfo
.getSuperInterfaces() : ArrayUtil
.EMPTY_INT_ARRAY
;
323 catch (Throwable e
) {
324 throw new CacheCorruptedException(e
);
328 public int getFlags(int classId
) throws CacheCorruptedException
{
330 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classId
);
331 return classInfo
!= null? classInfo
.getFlags() : UNKNOWN
;
333 catch (Throwable e
) {
334 throw new CacheCorruptedException(e
);
338 public FieldInfo
[] getFields(int qName
) throws CacheCorruptedException
{
340 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(qName
);
341 return classInfo
!= null? classInfo
.getFields() : FieldInfo
.EMPTY_ARRAY
;
343 catch (Throwable e
) {
344 throw new CacheCorruptedException(e
);
349 public FieldInfo
findField(final int classDeclarationId
, final int name
, final int descriptor
) throws CacheCorruptedException
{
351 for (FieldInfo fieldInfo
: getFields(classDeclarationId
)) {
352 if (fieldInfo
.getName() == name
&& fieldInfo
.getDescriptor() == descriptor
) {
358 catch (Throwable e
) {
359 throw new CacheCorruptedException(e
);
364 public FieldInfo
findFieldByName(final int classDeclarationId
, final int name
) throws CacheCorruptedException
{
366 for (FieldInfo fieldInfo
: getFields(classDeclarationId
)) {
367 if (fieldInfo
.getName() == name
) {
373 catch (Throwable e
) {
374 throw new CacheCorruptedException(e
);
378 public MethodInfo
[] getMethods(int classQName
) throws CacheCorruptedException
{
380 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(classQName
);
381 return classInfo
!= null? classInfo
.getMethods() : MethodInfo
.EMPTY_ARRAY
;
383 catch (Throwable e
) {
384 throw new CacheCorruptedException(e
);
389 public MethodInfo
findMethod(final int classQName
, final int name
, final int descriptor
) throws CacheCorruptedException
{
391 for (MethodInfo methodInfo
: getMethods(classQName
)) {
392 if (methodInfo
.getName() == name
&& methodInfo
.getDescriptor() == descriptor
) {
398 catch (Throwable e
) {
399 throw new CacheCorruptedException(e
);
403 public List
<MethodInfo
> findMethodsByName(final int classDeclarationId
, final int name
) throws CacheCorruptedException
{
405 final List
<MethodInfo
> methods
= new ArrayList
<MethodInfo
>();
406 for (MethodInfo methodInfo
: getMethods(classDeclarationId
)) {
407 if (methodInfo
.getName() == name
) {
408 methods
.add(methodInfo
);
413 catch (Throwable e
) {
414 throw new CacheCorruptedException(e
);
419 public MethodInfo
findMethodsBySignature(final int classDeclarationId
, final String signature
, SymbolTable symbolTable
) throws CacheCorruptedException
{
421 for (MethodInfo methodInfo
: getMethods(classDeclarationId
)) {
422 if (signature
.equals(CacheUtils
.getMethodSignature(symbolTable
.getSymbol(methodInfo
.getName()), symbolTable
.getSymbol(methodInfo
.getDescriptor())))) {
428 catch (Throwable e
) {
429 throw new CacheCorruptedException(e
);
433 public void addClassReferencer(int qName
, int referencerQName
) throws CacheCorruptedException
{
435 if (qName
== referencerQName
) {
436 return; // do not log self-dependencies
438 if (myQNameToClassInfoMap
.containsMapping(qName
)) {
439 myDependencies
.addClassReferencer(qName
, referencerQName
);
440 myQNameToReferencedClassesMap
.addValue(referencerQName
, qName
);
443 catch (Throwable e
) {
444 throw new CacheCorruptedException(e
);
448 public void removeClassReferencer(int qName
, int referencerQName
) throws CacheCorruptedException
{
450 myDependencies
.removeReferencer(qName
, referencerQName
);
452 catch (Throwable e
) {
453 throw new CacheCorruptedException(e
);
457 public void addFieldReferencer(int qName
, int fieldName
, int referencerQName
) throws CacheCorruptedException
{
459 if (qName
!= referencerQName
&& myQNameToClassInfoMap
.containsMapping(qName
)) {
460 myDependencies
.addFieldReferencer(qName
, referencerQName
, fieldName
);
461 myQNameToReferencedClassesMap
.addValue(referencerQName
, qName
);
464 catch (Throwable e
) {
465 throw new CacheCorruptedException(e
);
469 public void addMethodReferencer(int qName
, int methodName
, int methodDescriptor
, int referencerQName
) throws CacheCorruptedException
{
471 if (qName
!= referencerQName
&& myQNameToClassInfoMap
.containsMapping(qName
)) {
472 myDependencies
.addMethodReferencer(qName
, referencerQName
, methodName
, methodDescriptor
);
473 myQNameToReferencedClassesMap
.addValue(referencerQName
, qName
);
476 catch (Throwable e
) {
477 throw new CacheCorruptedException(e
);
482 public Dependency
[] getBackDependencies(final int classQName
) throws CacheCorruptedException
{
483 return myDependencies
.getDependencies(classQName
);
487 myDependencies
.dispose();
488 myQNameToReferencedClassesMap
.dispose();
489 myQNameToSubclassesMap
.dispose();
491 myQNameToClassInfoMap
.close();
493 catch (IOException e
) {
497 myRemoteQNames
.close();
499 catch (IOException e
) {
502 final File
[] files
= new File(myStorePath
).listFiles();
504 for (File file
: files
) {
505 if (!file
.isDirectory()) {
506 FileUtil
.delete(file
);
512 public void removeClass(final int qName
) throws CacheCorruptedException
{
514 final ClassInfo classInfo
= myQNameToClassInfoMap
.get(qName
);
515 if (classInfo
== null) {
518 myDependencies
.remove(qName
);
519 myQNameToClassInfoMap
.remove(qName
);
520 myQNameToReferencedClassesMap
.remove(qName
);
521 myQNameToSubclassesMap
.remove(qName
);
522 myRemoteQNames
.remove(qName
);
524 catch (Throwable e
) {
525 throw new CacheCorruptedException(e
);