crlfs
[fedora-idea.git] / java / compiler / impl / src / com / intellij / compiler / make / DependencyCache.java
blobb1d2ce8a01311a97b5ecb0ad8f1b0363eae37c60
1 /*
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.
17 /**
18 * created at Jan 7, 2002
19 * @author Jeka
21 package com.intellij.compiler.make;
23 import com.intellij.compiler.SymbolTable;
24 import com.intellij.compiler.classParsing.*;
25 import com.intellij.compiler.impl.CompilerUtil;
26 import com.intellij.compiler.impl.javaCompiler.DependencyProcessor;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.compiler.CompileContext;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.progress.ProcessCanceledException;
31 import com.intellij.openapi.progress.ProgressIndicator;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.Computable;
34 import com.intellij.openapi.util.Pair;
35 import com.intellij.openapi.vfs.VirtualFile;
36 import com.intellij.util.ArrayUtil;
37 import com.intellij.util.cls.ClsFormatException;
38 import com.intellij.util.cls.ClsUtil;
39 import gnu.trove.TIntHashSet;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
44 import java.io.File;
45 import java.io.IOException;
46 import java.rmi.Remote;
47 import java.util.*;
49 public class DependencyCache {
50 private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.DependencyCache");
52 private Cache myCache;
53 private Cache myNewClassesCache;
55 private static final String REMOTE_INTERFACE_NAME = Remote.class.getName();
56 private TIntHashSet myToUpdate = new TIntHashSet(); // qName strings to be updated.
57 private final TIntHashSet myTraverseRoots = new TIntHashSet(); // Dependencies are calculated from these clasess
58 private final TIntHashSet myClassesWithSourceRemoved = new TIntHashSet();
59 private final TIntHashSet myPreviouslyRemoteClasses = new TIntHashSet(); // classes that were Remote, but became non-Remote for some reason
60 private final TIntHashSet myMarkedInfos = new TIntHashSet(); // classes to be recompiled
61 private final Set<VirtualFile> myMarkedFiles = new HashSet<VirtualFile>();
63 private DependencyCacheNavigator myCacheNavigator;
64 private SymbolTable mySymbolTable;
65 private final String mySymbolTableFilePath;
66 private final String myStoreDirectoryPath;
67 @NonNls private static final String SYMBOLTABLE_FILE_NAME = "symboltable.dat";
69 public DependencyCache(@NonNls String storeDirectoryPath) {
70 myStoreDirectoryPath = storeDirectoryPath;
71 LOG.assertTrue(myStoreDirectoryPath != null);
73 mySymbolTableFilePath = myStoreDirectoryPath + "/" + SYMBOLTABLE_FILE_NAME;
76 public DependencyCacheNavigator getCacheNavigator() throws CacheCorruptedException {
77 if (myCacheNavigator == null) {
78 myCacheNavigator = new DependencyCacheNavigator(getCache());
80 return myCacheNavigator;
83 public void wipe() throws CacheCorruptedException {
84 getCache().wipe();
85 getNewClassesCache().wipe();
88 public Cache getCache() throws CacheCorruptedException {
89 try {
90 if (myCache == null) {
91 // base number of cached record views of each type
92 myCache = new Cache(myStoreDirectoryPath, 512);
95 return myCache;
97 catch (IOException e) {
98 throw new CacheCorruptedException(e);
102 public Cache getNewClassesCache() throws CacheCorruptedException {
103 try {
104 if (myNewClassesCache == null) {
105 myNewClassesCache = new Cache(myStoreDirectoryPath + "/tmp", 16);
107 return myNewClassesCache;
109 catch (IOException e) {
110 throw new CacheCorruptedException(e);
114 public void addTraverseRoot(int qName) {
115 myTraverseRoots.add(qName);
118 public void clearTraverseRoots() {
119 myTraverseRoots.clear();
122 public void markSourceRemoved(int qName) {
123 myClassesWithSourceRemoved.add(qName);
126 public void addClassToUpdate(int qName) {
127 myToUpdate.add(qName);
130 public int reparseClassFile(@NotNull File file, final byte[] fileContent) throws ClsFormatException, CacheCorruptedException {
131 SymbolTable symbolTable = getSymbolTable();
133 final int qName = getNewClassesCache().importClassInfo(new ClassFileReader(file, symbolTable, fileContent), symbolTable);
134 addClassToUpdate(qName);
135 addTraverseRoot(qName);
136 return qName;
139 // for profiling purposes
141 private static void pause() {
142 System.out.println("PAUSED. ENTER A CHAR.");
143 byte[] buf = new byte[1];
144 try {
145 System.in.read(buf);
147 catch (IOException e) {
148 e.printStackTrace();
153 public void update(ProgressIndicator indicator) throws CacheCorruptedException {
154 if (myToUpdate.isEmpty()) {
155 return; // optimization
158 final long updateStart = System.currentTimeMillis();
159 //pause();
161 final int[] namesToUpdate = myToUpdate.toArray();
162 final Cache cache = getCache();
163 final Cache newCache = getNewClassesCache();
164 final DependencyCacheNavigator navigator = getCacheNavigator();
166 int i = 0;
167 // remove unnecesary dependencies
168 for (final int qName : namesToUpdate) {
169 indicator.setFraction(i++*1.0/namesToUpdate.length/4);
170 // process use-dependencies
171 for (int referencedClassQName : cache.getReferencedClasses(qName)) {
172 if (!cache.containsClass(referencedClassQName)) {
173 continue;
175 cache.removeClassReferencer(referencedClassQName, qName);
177 cache.clearReferencedClasses(qName);
178 // process inheritance dependencies
179 navigator.walkSuperClasses(qName, new ClassInfoProcessor() {
180 public boolean process(int classQName) throws CacheCorruptedException {
181 cache.removeSubclass(classQName, qName);
182 return true;
187 // do update of classInfos
188 for (final int qName : namesToUpdate) {
189 indicator.setFraction(i++*1.0/namesToUpdate.length/4);
190 cache.importClassInfo(newCache, qName);
193 // build forward-dependencies for the new infos, all new class infos must be already in the main cache!
195 final SymbolTable symbolTable = getSymbolTable();
197 for (final int qName : namesToUpdate) {
198 indicator.setFraction(i++*1.0/namesToUpdate.length/4);
199 if (!newCache.containsClass(qName)) {
200 continue;
202 buildForwardDependencies(qName, newCache.getReferences(qName));
203 boolean isRemote = false;
204 // "remote objects" are classes that _directly_ implement remote interfaces
205 final int[] superInterfaces = cache.getSuperInterfaces(qName);
206 if (superInterfaces.length > 0) {
207 final int remoteInterfaceName = symbolTable.getId(REMOTE_INTERFACE_NAME);
208 for (int superInterface : superInterfaces) {
209 if (isRemoteInterface(cache, superInterface, remoteInterfaceName)) {
210 isRemote = true;
211 break;
215 final boolean wasRemote = cache.isRemote(qName);
216 if (wasRemote && !isRemote) {
217 synchronized (myPreviouslyRemoteClasses) {
218 myPreviouslyRemoteClasses.add(qName);
221 cache.setRemote(qName, isRemote);
224 // building subclass dependencies
225 for (final int qName : namesToUpdate) {
226 indicator.setFraction(i++*1.0/namesToUpdate.length/4);
227 final int classId = qName;
228 buildSubclassDependencies(getCache(), qName, classId);
231 for (final int qName : myClassesWithSourceRemoved.toArray()) {
232 cache.removeClass(qName);
234 myToUpdate = new TIntHashSet();
236 CompilerUtil.logDuration("Dependency cache update", System.currentTimeMillis() - updateStart);
237 //pause();
240 private void buildForwardDependencies(final int classQName, final Collection<ReferenceInfo> references) throws CacheCorruptedException {
241 final Cache cache = getCache();
243 final int genericSignature = cache.getGenericSignature(classQName);
244 if (genericSignature != -1) {
245 final String genericClassSignature = resolve(genericSignature);
246 final int[] bounds = findBounds(genericClassSignature);
247 for (int boundClassQName : bounds) {
248 cache.addClassReferencer(boundClassQName, classQName);
252 buildAnnotationDependencies(classQName, cache.getRuntimeVisibleAnnotations(classQName));
253 buildAnnotationDependencies(classQName, cache.getRuntimeInvisibleAnnotations(classQName));
255 for (final ReferenceInfo refInfo : references) {
256 final int declaringClassName = getActualDeclaringClassForReference(refInfo);
257 if (declaringClassName == Cache.UNKNOWN) {
258 continue;
260 if (refInfo instanceof MemberReferenceInfo) {
261 final MemberInfo memberInfo = ((MemberReferenceInfo)refInfo).getMemberInfo();
262 if (memberInfo instanceof FieldInfo) {
263 cache.addFieldReferencer(declaringClassName, memberInfo.getName(), classQName);
265 else if (memberInfo instanceof MethodInfo) {
266 cache.addMethodReferencer(declaringClassName, memberInfo.getName(), memberInfo.getDescriptor(), classQName);
268 else {
269 LOG.error("Unknown member info class: " + memberInfo.getClass().getName());
272 else { // reference to class
273 cache.addClassReferencer(declaringClassName, classQName);
276 final SymbolTable symbolTable = getSymbolTable();
278 for (final FieldInfo fieldInfo : cache.getFields(classQName)) {
279 buildAnnotationDependencies(classQName, fieldInfo.getRuntimeVisibleAnnotations());
280 buildAnnotationDependencies(classQName, fieldInfo.getRuntimeInvisibleAnnotations());
282 String className = MakeUtil.parseObjectType(symbolTable.getSymbol(fieldInfo.getDescriptor()), 0);
283 if (className == null) {
284 continue;
286 final int cls = symbolTable.getId(className);
287 cache.addClassReferencer(cls, classQName);
290 for (final MethodInfo methodInfo : cache.getMethods(classQName)) {
291 buildAnnotationDependencies(classQName, methodInfo.getRuntimeVisibleAnnotations());
292 buildAnnotationDependencies(classQName, methodInfo.getRuntimeInvisibleAnnotations());
293 buildAnnotationDependencies(classQName, methodInfo.getRuntimeVisibleParameterAnnotations());
294 buildAnnotationDependencies(classQName, methodInfo.getRuntimeInvisibleParameterAnnotations());
296 if (methodInfo.isConstructor()) {
297 continue;
300 final String returnTypeClassName = MakeUtil.parseObjectType(methodInfo.getReturnTypeDescriptor(symbolTable), 0);
301 if (returnTypeClassName != null) {
302 final int returnTypeClassQName = symbolTable.getId(returnTypeClassName);
303 cache.addClassReferencer(returnTypeClassQName, classQName);
306 String[] parameterSignatures = CacheUtils.getParameterSignatures(methodInfo, symbolTable);
307 for (String parameterSignature : parameterSignatures) {
308 String paramClassName = MakeUtil.parseObjectType(parameterSignature, 0);
309 if (paramClassName != null) {
310 final int paramClassId = symbolTable.getId(paramClassName);
311 cache.addClassReferencer(paramClassId, classQName);
317 private static boolean isRemoteInterface(Cache cache, int ifaceName, final int remoteInterfaceName) throws CacheCorruptedException {
318 if (ifaceName == remoteInterfaceName) {
319 return true;
321 for (int superInterfaceName : cache.getSuperInterfaces(ifaceName)) {
322 if (isRemoteInterface(cache, superInterfaceName, remoteInterfaceName)) {
323 return true;
326 return false;
330 private void buildAnnotationDependencies(int classQName, AnnotationConstantValue[][] annotations) throws CacheCorruptedException {
331 if (annotations == null || annotations.length == 0) {
332 return;
334 for (AnnotationConstantValue[] annotation : annotations) {
335 buildAnnotationDependencies(classQName, annotation);
339 private void buildAnnotationDependencies(int classQName, AnnotationConstantValue[] annotations) throws CacheCorruptedException {
340 if (annotations == null || annotations.length == 0) {
341 return;
343 final Cache cache = getCache();
344 for (AnnotationConstantValue annotation : annotations) {
345 final int annotationQName = annotation.getAnnotationQName();
347 cache.addClassReferencer(annotationQName, classQName);
349 final AnnotationNameValuePair[] memberValues = annotation.getMemberValues();
350 for (final AnnotationNameValuePair nameValuePair : memberValues) {
351 for (MethodInfo annotationMember : cache.findMethodsByName(annotationQName, nameValuePair.getName())) {
352 cache.addMethodReferencer(annotationQName, annotationMember.getName(), annotationMember.getDescriptor(), classQName);
358 private int[] findBounds(final String genericClassSignature) throws CacheCorruptedException{
359 try {
360 final String[] boundInterfaces = BoundsParser.getBounds(genericClassSignature);
361 int[] ids = ArrayUtil.newIntArray(boundInterfaces.length);
362 for (int i = 0; i < boundInterfaces.length; i++) {
363 ids[i] = getSymbolTable().getId(boundInterfaces[i]);
365 return ids;
367 catch (SignatureParsingException e) {
368 return ArrayUtil.EMPTY_INT_ARRAY;
372 // fixes JDK 1.4 javac bug that generates references in the constant pool
373 // to the subclass even if the field was declared in a superclass
374 private int getActualDeclaringClassForReference(final ReferenceInfo refInfo) throws CacheCorruptedException {
375 if (!(refInfo instanceof MemberReferenceInfo)) {
376 return refInfo.getClassName();
378 final int declaringClassName = refInfo.getClassName();
379 final Cache cache = getCache();
380 final MemberInfo memberInfo = ((MemberReferenceInfo)refInfo).getMemberInfo();
381 if (memberInfo instanceof FieldInfo) {
382 if (cache.findFieldByName(declaringClassName, memberInfo.getName()) != null) {
383 return declaringClassName;
386 else if (memberInfo instanceof MethodInfo) {
387 if (cache.findMethod(declaringClassName, memberInfo.getName(), memberInfo.getDescriptor()) != null) {
388 return declaringClassName;
391 final DeclaringClassFinder finder = new DeclaringClassFinder(memberInfo);
392 getCacheNavigator().walkSuperClasses(declaringClassName, finder);
393 return finder.getDeclaringClassName();
397 * @return qualified names of the classes that should be additionally recompiled
399 public Pair<int[], Set<VirtualFile>> findDependentClasses(CompileContext context, Project project, Set<VirtualFile> successfullyCompiled, @Nullable final DependencyProcessor additionalProcessor)
400 throws CacheCorruptedException {
402 markDependencies(context, project, successfullyCompiled, additionalProcessor);
403 return new Pair<int[], Set<VirtualFile>>(myMarkedInfos.toArray(), Collections.unmodifiableSet(myMarkedFiles));
406 private void markDependencies(CompileContext context, Project project, final Set<VirtualFile> successfullyCompiled,
407 @Nullable final DependencyProcessor additionalProcessor) throws CacheCorruptedException {
408 try {
409 if (LOG.isDebugEnabled()) {
410 LOG.debug("====================Marking dependent files=====================");
412 // myToUpdate can be modified during the mark procedure, so use toArray() to iterate it
413 int[] qNamesToUpdate = myTraverseRoots.toArray();
414 final SourceFileFinder sourceFileFinder = new SourceFileFinder(project, context);
415 final CachingSearcher searcher = new CachingSearcher(project);
416 final ChangedRetentionPolicyDependencyProcessor changedRetentionPolicyDependencyProcessor = new ChangedRetentionPolicyDependencyProcessor(project, searcher, this);
417 for (final int qName : qNamesToUpdate) {
418 if (!getCache().containsClass(qName)) {
419 continue;
421 if (getNewClassesCache().containsClass(qName)) { // there is a new class file created
422 new JavaDependencyProcessor(project, this, qName).run();
423 ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo> changed =
424 new ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo>();
425 ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo> removed =
426 new ArrayList<ChangedConstantsDependencyProcessor.FieldChangeInfo>();
427 findModifiedConstants(qName, changed, removed);
428 if (!changed.isEmpty() || !removed.isEmpty()) {
429 new ChangedConstantsDependencyProcessor(
430 project, searcher, this, qName,
431 changed.toArray(new ChangedConstantsDependencyProcessor.FieldChangeInfo[changed.size()]),
432 removed.toArray(new ChangedConstantsDependencyProcessor.FieldChangeInfo[removed.size()])
433 ).run();
435 changedRetentionPolicyDependencyProcessor.checkAnnotationRetentionPolicyChanges(qName);
436 if (additionalProcessor != null) {
437 additionalProcessor.processDependencies(context, qName);
440 else {
441 boolean isSourceDeleted = false;
442 if (myClassesWithSourceRemoved.contains(qName)) { // no recompiled class file, check whether the classfile exists
443 isSourceDeleted = true;
445 else if (!new File(getCache().getPath(qName)).exists()) {
446 final String qualifiedName = resolve(qName);
447 final String sourceFileName = getCache().getSourceFileName(qName);
448 final boolean markAsRemovedSource = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
449 public Boolean compute() {
450 VirtualFile sourceFile = sourceFileFinder.findSourceFile(qualifiedName, sourceFileName);
451 return sourceFile == null || successfullyCompiled.contains(sourceFile) ? Boolean.TRUE : Boolean.FALSE;
453 }).booleanValue();
454 if (markAsRemovedSource) {
455 // for Inner classes: sourceFile may exist, but the inner class declaration inside it may not,
456 // thus the source for the class info should be considered removed
457 isSourceDeleted = true;
458 markSourceRemoved(qName);
459 myMarkedInfos.remove(qName); // if the info has been marked already, the mark should be removed
462 if (isSourceDeleted) {
463 Dependency[] backDependencies = getCache().getBackDependencies(qName);
464 for (Dependency backDependency : backDependencies) {
465 if (markTargetClassInfo(backDependency)) {
466 if (LOG.isDebugEnabled()) {
467 LOG.debug(
468 "Mark dependent class " + backDependency.getClassQualifiedName() + "; reason: no class file found for " + qName);
475 if (LOG.isDebugEnabled()) {
476 LOG.debug("================================================================");
479 catch (ProcessCanceledException ignored) {
480 // deliberately suppressed
484 private void findModifiedConstants(
485 final int qName,
486 Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> changedConstants,
487 Collection<ChangedConstantsDependencyProcessor.FieldChangeInfo> removedConstants) throws CacheCorruptedException {
489 final Cache cache = getCache();
490 for (final FieldInfo field : cache.getFields(qName)) {
491 final int oldFlags = field.getFlags();
492 if (ClsUtil.isStatic(oldFlags) && ClsUtil.isFinal(oldFlags)) {
493 final Cache newClassesCache = getNewClassesCache();
494 FieldInfo newField = newClassesCache.findFieldByName(qName, field.getName());
495 if (newField == null) {
496 if (!ConstantValue.EMPTY_CONSTANT_VALUE.equals(field.getConstantValue())) {
497 // if the field was really compile time constant
498 removedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field));
501 else {
502 final boolean visibilityRestricted = MakeUtil.isMoreAccessible(oldFlags, newField.getFlags());
503 if (!field.getConstantValue().equals(newField.getConstantValue()) || visibilityRestricted) {
504 changedConstants.add(new ChangedConstantsDependencyProcessor.FieldChangeInfo(field, visibilityRestricted));
511 private static void buildSubclassDependencies(Cache cache, final int qName, int targetClassId) throws CacheCorruptedException {
512 final int superQName = cache.getSuperQualifiedName(targetClassId);
513 if (superQName != Cache.UNKNOWN) {
514 cache.addSubclass(superQName, qName);
515 buildSubclassDependencies(cache, qName, superQName);
518 int[] interfaces = cache.getSuperInterfaces(targetClassId);
519 for (final int interfaceName : interfaces) {
520 cache.addSubclass(interfaceName, qName);
521 buildSubclassDependencies(cache, qName, interfaceName);
527 * Marks ClassInfo targeted by the dependency
528 * @return true if really added, false otherwise
530 public boolean markTargetClassInfo(Dependency dependency) throws CacheCorruptedException {
531 return markClassInfo(dependency.getClassQualifiedName(), false);
535 * Marks ClassInfo that corresponds to the specified qualified name
536 * If class info is already recompiled, it is not marked
537 * @return true if really added, false otherwise
539 public boolean markClass(int qualifiedName) throws CacheCorruptedException {
540 return markClass(qualifiedName, false);
544 * Marks ClassInfo that corresponds to the specified qualified name
545 * If class info is already recompiled, it is not marked unless force parameter is true
546 * @return true if really added, false otherwise
548 public boolean markClass(int qualifiedName, boolean force) throws CacheCorruptedException {
549 return markClassInfo(qualifiedName, force);
552 public boolean isTargetClassInfoMarked(Dependency dependency) {
553 return isClassInfoMarked(dependency.getClassQualifiedName());
556 public boolean isClassInfoMarked(int qName) {
557 return myMarkedInfos.contains(qName);
560 public void markFile(VirtualFile file) {
561 myMarkedFiles.add(file);
565 * @return true if really marked, false otherwise
567 private boolean markClassInfo(int qName, boolean force) throws CacheCorruptedException {
568 if (!getCache().containsClass(qName)) {
569 return false;
571 if (myClassesWithSourceRemoved.contains(qName)) {
572 return false; // no need to recompile since source has been removed
574 if (!force) {
575 if (getNewClassesCache().containsClass(qName)) { // already recompiled
576 return false;
579 return myMarkedInfos.add(qName);
582 public void resetState() {
583 final long start = System.currentTimeMillis();
585 try {
586 myClassesWithSourceRemoved.clear();
587 myMarkedFiles.clear();
588 myMarkedInfos.clear();
589 myToUpdate.clear();
590 myTraverseRoots.clear();
591 if (myNewClassesCache != null) {
592 myNewClassesCache.wipe();
593 myNewClassesCache = null;
595 myCacheNavigator = null;
596 try {
597 if (myCache != null) {
598 myCache.dispose();
599 myCache = null;
602 catch (CacheCorruptedException e) {
603 LOG.info(e);
605 try {
606 if (mySymbolTable != null) {
607 mySymbolTable.dispose();
608 mySymbolTable = null;
611 catch (CacheCorruptedException e) {
612 LOG.info(e);
615 finally {
616 CompilerUtil.logDuration("Dependency cache disposal", System.currentTimeMillis() - start);
621 public SymbolTable getSymbolTable() throws CacheCorruptedException {
622 if (mySymbolTable == null) {
623 mySymbolTable = new SymbolTable(new File(mySymbolTableFilePath));
625 return mySymbolTable;
628 public String resolve(int id) throws CacheCorruptedException {
629 return getSymbolTable().getSymbol(id);
632 public boolean wasRemote(int qName) {
633 return myPreviouslyRemoteClasses.contains(qName);
636 private class DeclaringClassFinder implements ClassInfoProcessor {
637 private final int myMemberName;
638 private final int myMemberDescriptor;
639 private int myDeclaringClass = Cache.UNKNOWN;
640 private final boolean myIsField;
642 private DeclaringClassFinder(MemberInfo memberInfo) {
643 myMemberName = memberInfo.getName();
644 myMemberDescriptor = memberInfo.getDescriptor();
645 myIsField = memberInfo instanceof FieldInfo;
648 public int getDeclaringClassName() {
649 return myDeclaringClass;
652 public boolean process(int classQName) throws CacheCorruptedException {
653 final Cache cache = getCache();
654 if (myIsField) {
655 final FieldInfo fieldId = cache.findField(classQName, myMemberName, myMemberDescriptor);
656 if (fieldId != null) {
657 myDeclaringClass = classQName;
658 return false;
661 else {
662 final MethodInfo methodId = cache.findMethod(classQName, myMemberName, myMemberDescriptor);
663 if (methodId != null) {
664 myDeclaringClass = classQName;
665 return false;
668 return true;