void (DifferentiateContext context, JvmClass changedClass, Iterable<Difference.Change<JvmMethod, JvmMethod.Diff>> changed, Utils future, Utils present) { debug("Processing changed methods: "); if (changedClass.isInterface()) { for (Difference.Change<JvmMethod, JvmMethod.Diff> change : Iterators.filter(changed, ch -> ch.getDiff().getRemovedFlags().isAbstract())) { debug("Method became non-abstract: ", change.getPast().getName()); affectLambdaInstantiations(context, present, changedClass.getReferenceID()); break; } } for (Difference.Change<JvmMethod, JvmMethod.Diff> change : changed) { JvmMethod changedMethod = change.getPast(); JvmMethod.Diff diff = change.getDiff(); if (diff.unchanged()) { continue; } debug("Method: ", changedMethod.getName()); if (changedClass.isAnnotation()) { if (diff.valueRemoved()) { debug("Class is annotation, default value is removed => adding annotation query"); String argName = changedMethod.getName(); TypeRepr.ClassType annotType = new TypeRepr.ClassType(changedClass.getName()); context.affectUsage((node, usage) -> { if (usage instanceof AnnotationUsage) { // need to find annotation usages that do not use arguments this annotation uses AnnotationUsage au = (AnnotationUsage)usage; return annotType.equals(au.getClassType()) && Iterators.isEmpty(Iterators.filter(au.getUsedArgNames(), argName::equals)); } return false; }); } continue; } Iterable<JvmNodeReferenceID> propagated = Iterators.lazy(() -> future.collectSubclassesWithoutMethod(changedClass.getReferenceID(), changedMethod)); if (diff.becamePackageLocal()) { debug("Method became package-private, affecting method usages outside the package"); affectMemberUsages(context, changedClass.getReferenceID(), changedMethod, propagated, new PackageConstraint(changedClass.getPackageName())); } if (diff.typeChanged() || diff.signatureChanged() || !diff.exceptions().unchanged()) { debug("Return type, throws list or signature changed --- affecting method usages"); affectMemberUsages(context, changedClass.getReferenceID(), changedMethod, propagated); for (JvmNodeReferenceID subClass : Iterators.unique(Iterators.map(future.getOverridingMethods(changedClass, changedMethod, changedMethod::isSameByJavaRules), p -> p.getFirst().getReferenceID()))) { affectNodeSources(context, subClass, "Affect source file of a class which overrides the changed method: "); } } else if (diff.flagsChanged()) { JVMFlags addedFlags = diff.getAddedFlags(); JVMFlags removedFlags = diff.getRemovedFlags(); if (addedFlags.isStatic() || addedFlags.isPrivate() || addedFlags.isSynthetic() || addedFlags.isBridge() || removedFlags.isStatic()) { // 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 debug("Added {static | private | synthetic | bridge} specifier or removed static specifier --- affecting method usages"); affectMemberUsages(context, changedClass.getReferenceID(), changedMethod, propagated); if (addedFlags.isStatic()) { debug("Added static specifier --- affecting subclasses"); affectSubclasses(context, future, changedClass.getReferenceID(), false); if (!changedMethod.isPrivate()) { debug("Added static modifier --- affecting static member on-demand import usages"); affectStaticMemberOnDemandUsages(context, changedClass.getReferenceID(), propagated); } } else if (removedFlags.isStatic()) { if (!changedMethod.isPrivate()) { debug("Removed static modifier --- affecting static method import usages"); affectStaticMemberImportUsages(context, changedClass.getReferenceID(), changedMethod.getName(), propagated); } } } else { if (addedFlags.isFinal() || addedFlags.isPublic() || addedFlags.isAbstract()) { debug("Added final, public or abstract specifier --- affecting subclasses"); affectSubclasses(context, future, changedClass.getReferenceID(), false); if (changedClass.isInterface() && addedFlags.isAbstract()) { affectLambdaInstantiations(context, present, changedClass.getReferenceID()); } } if (addedFlags.isProtected() && !removedFlags.isPrivate()) { debug("Added public or package-private method became protected --- affect method usages with protected constraint"); affectMemberUsages(context, changedClass.getReferenceID(), changedMethod, propagated, new InheritanceConstraint(future, changedClass)); } } } // todo: do we need to support AnnotationChangeTracker in the new implementation? Looks like its functionality transforms into kotlin-specific rules //Difference.Specifier<TypeRepr.ClassType, ?> annotationsDiff = diff.annotations(); //if (!annotationsDiff.unchanged()) { //} } Iterable<Difference.Change<JvmMethod, JvmMethod.Diff>> moreAccessible = Iterators.collect(Iterators.filter(changed, ch -> ch.getDiff().accessExpanded()), new SmartList<>()); if (!Iterators.isEmpty(moreAccessible)) { Iterable<OverloadDescriptor> overloaded = findAllOverloads(future, changedClass, method -> { JVMFlags mostAccessible = null; for (var change : moreAccessible) { JvmMethod m = change.getNow(); if (Objects.equals(m.getName(), method.getName()) && !m.isSame(method)) { if (mostAccessible == null || mostAccessible.isWeakerAccess(m.getFlags())) { mostAccessible = m.getFlags(); } } } return mostAccessible; }); for (OverloadDescriptor descr : overloaded) { debug("Method became more accessible --- affect usages of overloading methods: ", descr.overloadMethod.getName()); Predicate<Node<?, ?>> constr = descr.accessScope.isPackageLocal()? new PackageConstraint(changedClass.getPackageName()).negate() : descr.accessScope.isProtected()? new InheritanceConstraint(future, changedClass).negate() : null; affectMemberUsages(context, descr.owner, descr.overloadMethod, future.collectSubclassesWithoutMethod(descr.owner, descr.overloadMethod), constr); } } debug("End of changed methods processing"); }