void (final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { final Collection<Pair<MethodRepr, MethodRepr.Diff>> changed = diff.methods().changed(); if (changed.isEmpty()) { return; } debug("Processing changed methods:"); assert myPresent != null; assert myFuture != null; assert myAffectedFiles != null; if (it.isInterface()) { for (final Pair<MethodRepr, MethodRepr.Diff> mr : changed) { if ((mr.second.removedModifiers() & Opcodes.ACC_ABSTRACT) != 0) { debug("Method became non-abstract: ", mr.first.name); myPresent.affectLambdaInstantiations(state, it.name); break; } } } final List<Pair<MethodRepr, MethodRepr.Diff>> moreAccessible = new ArrayList<>(); for (final Pair<MethodRepr, MethodRepr.Diff> mr : changed) { final MethodRepr m = mr.first; final MethodRepr.Diff d = mr.second; final boolean throwsChanged = !d.exceptions().unchanged(); debug("Method: ", m.name); if (d.accessExpanded()) { moreAccessible.add(mr); } if (it.isAnnotation()) { if (d.defaultRemoved()) { debug("Class is annotation, default value is removed => adding annotation query"); IntSet l = new IntOpenHashSet(DEFAULT_SET_CAPACITY, DEFAULT_SET_LOAD_FACTOR); l.add(m.name); final UsageRepr.AnnotationUsage annotationUsage = (UsageRepr.AnnotationUsage)UsageRepr .createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, it.name), l, null); state.myAnnotationQuery.add(annotationUsage); } } else if (d.base() != Difference.NONE || throwsChanged) { final Supplier<IntSet> propagated = lazy(()-> myFuture.propagateMethodAccess(m, it.name)); boolean affected = false; boolean constrained = false; final Set<UsageRepr.Usage> usages = new HashSet<>(); if (d.packageLocalOn()) { debug("Method became package-private, affecting method usages outside the package"); myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants); for (final UsageRepr.Usage usage : usages) { state.myUsageConstraints.put(usage, myFuture.new PackageConstraint(it.getPackageName())); } state.myAffectedUsages.addAll(usages); affected = true; constrained = true; } if ((d.base() & Difference.TYPE) != 0 || (d.base() & Difference.SIGNATURE) != 0 || throwsChanged) { if (!affected) { debug("Return type, throws list or signature changed --- affecting method usages"); myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants); final List<Pair<MethodRepr, ClassRepr>> overridingMethods = new LinkedList<>(); myFuture.addOverridingMethods(m, it, it, MethodRepr.equalByJavaRules(m), overridingMethods, null); for(final Pair<MethodRepr, ClassRepr> p : overridingMethods) { final ClassRepr aClass = p.getSecond(); if (aClass != MOCK_CLASS) { final Iterable<File> fileNames = classToSourceFileGet(aClass.name); if (fileNames != null) { ContainerUtil.addAll(myAffectedFiles, fileNames); } } } state.myAffectedUsages.addAll(usages); affected = true; } } else if ((d.base() & Difference.ACCESS) != 0) { if ((d.addedModifiers() & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_BRIDGE)) != 0 || (d.removedModifiers() & Opcodes.ACC_STATIC) != 0) { // When synthetic or bridge flags are added, this effectively means that explicitly written in the code // method with the same signature and return type has been removed and a bridge method has been generated instead. // In some cases (e.g. using raw types) the presence of such synthetic methods in the bytecode is ignored by the compiler // so that the code that called such method via raw type reference might not compile anymore => to be on the safe side // we should recompile all places where the method was used if (!affected) { debug("Added {static | private | synthetic | bridge} specifier or removed static specifier --- affecting method usages"); myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); affected = true; } if ((d.addedModifiers() & Opcodes.ACC_STATIC) != 0) { debug("Added static specifier --- affecting subclasses"); myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); if (!m.isPrivate()) { debug("Added static modifier --- affecting static member on-demand import usages"); myFuture.affectStaticMemberOnDemandUsages(it.name, propagated.get(), state.myAffectedUsages, state.myDependants); } } else if ((d.removedModifiers() & Opcodes.ACC_STATIC) != 0) { if (!m.isPrivate()) { debug("Removed static modifier --- affecting static method import usages"); myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated.get(), state.myAffectedUsages, state.myDependants); } } } else { if ((d.addedModifiers() & (Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT)) != 0) { debug("Added final, public or abstract specifier --- affecting subclasses"); myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); if (it.isInterface() && (d.addedModifiers() & Opcodes.ACC_ABSTRACT) != 0) { myPresent.affectLambdaInstantiations(state, it.name); } } if ((d.addedModifiers() & Opcodes.ACC_PROTECTED) != 0 && (d.removedModifiers() & Opcodes.ACC_PRIVATE) == 0) { debug("Added public or package-private method became protected --- affect method usages with protected constraint"); if (!affected) { myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); affected = true; } for (final UsageRepr.Usage usage : usages) { state.myUsageConstraints.put(usage, myFuture.new InheritanceConstraint(it)); } constrained = true; } } } if ((d.base() & Difference.ANNOTATIONS) != 0) { final Set<AnnotationsChangeTracker.Recompile> toRecompile = EnumSet.noneOf(AnnotationsChangeTracker.Recompile.class); for (AnnotationsChangeTracker extension : myAnnotationChangeTracker) { if (toRecompile.containsAll(AnnotationsChangeTracker.RECOMPILE_ALL)) { break; } final Set<AnnotationsChangeTracker.Recompile> actions = extension.methodAnnotationsChanged(myContext, (ProtoMethodEntity)m, d.annotations(), d.parameterAnnotations()); if (actions.contains(AnnotationsChangeTracker.Recompile.USAGES)) { debug("Extension "+extension.getClass().getName()+" requested recompilation because of changes in annotations list --- affecting method usages"); } if (actions.contains(AnnotationsChangeTracker.Recompile.SUBCLASSES)) { debug("Extension "+extension.getClass().getName()+" requested recompilation because of changes in method annotations or method parameter annotations list --- affecting subclasses"); } toRecompile.addAll(actions); } if (toRecompile.contains(AnnotationsChangeTracker.Recompile.USAGES)) { myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); if (constrained) { // remove any constraints so that all usages of this method are recompiled for (UsageRepr.Usage usage : usages) { state.myUsageConstraints.remove(usage); } } } if (toRecompile.contains(AnnotationsChangeTracker.Recompile.SUBCLASSES)) { myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null); } } } } if (!moreAccessible.isEmpty()) { final Iterable<OverloadDescriptor> allOverloads = myFuture.findAllOverloads(it, mr -> { Integer result = null; for (Pair<MethodRepr, MethodRepr.Diff> pair : moreAccessible) { MethodRepr m = pair.first; MethodRepr.Diff d = pair.second; if (mr.name == m.name && !m.equals(mr)) { int newAccess = m.access & (~d.removedModifiers()) | d.addedModifiers(); if (result == null || Difference.weakerAccess(result, newAccess)) { result = newAccess; } } } return result; }); for (OverloadDescriptor descr : allOverloads) { debug("Method became more accessible --- affect usages of overloading methods: ", descr.overloadMethod.name); final Set<UsageRepr.Usage> overloadsUsages = new HashSet<>(); myFuture.affectMethodUsages( descr.overloadMethod, myFuture.propagateMethodAccess(descr.overloadMethod, descr.overloadMethodOwner.name), descr.overloadMethod.createUsage(myContext, descr.overloadMethodOwner.name), overloadsUsages, state.myDependants ); state.myAffectedUsages.addAll(overloadsUsages); final UsageConstraint constr = Difference.isPackageLocal(descr.accessScope)? myFuture.new PackageConstraint(it.getPackageName()).negate() : Difference.isProtected(descr.accessScope)? myFuture.new InheritanceConstraint(it).negate() : null; if (constr != null) { for (final UsageRepr.Usage usage : overloadsUsages) { state.myUsageConstraints.put(usage, constr); } } } } debug("End of changed methods processing"); }