soot-oss / soot

Soot - A Java optimization framework
GNU Lesser General Public License v2.1
2.89k stars 710 forks source link

ArrayTypeCache recursion exception (fix) #2119

Open eortega-pjr opened 3 weeks ago

eortega-pjr commented 3 weeks ago

20baf2f67aecf44f6089a0a42d7e9b2e08f4b449 introduced an ArrayTypeCache which evidences the following issue when processing certain inputs:

DEBUG: Caused by: java.lang.IllegalStateException: Recursive update
DEBUG:     at java.base/java.util.concurrent.ConcurrentHashMap.transfer(ConcurrentHashMap.java:2552)
DEBUG:     at java.base/java.util.concurrent.ConcurrentHashMap.addCount(ConcurrentHashMap.java:2354)
DEBUG:     at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1776)
DEBUG:     at soot.ArrayTypeCache$1.apply(ArrayTypeCache.java:54)
DEBUG:     at soot.ArrayTypeCache$1.apply(ArrayTypeCache.java:41)
DEBUG:     at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
DEBUG:     at soot.ArrayTypeCache.getArrayType(ArrayTypeCache.java:82)
DEBUG:     at soot.ArrayType.v(ArrayType.java:73)
DEBUG:     at soot.asm.AsmUtil.toJimpleType(AsmUtil.java:208)
DEBUG:     at soot.asm.SootClassBuilder.visitField(SootClassBuilder.java:166)
DEBUG:     at org.objectweb.asm.ClassReader.readField(ClassReader.java:1138)
DEBUG:     at org.objectweb.asm.ClassReader.accept(ClassReader.java:740)
DEBUG:     at org.objectweb.asm.ClassReader.accept(ClassReader.java:425)
DEBUG:     at soot.asm.AsmClassSource.resolve(AsmClassSource.java:67)
DEBUG:     at soot.SootResolver.bringToHierarchyUnchecked(SootResolver.java:258)
DEBUG:     at soot.SootResolver.bringToHierarchy(SootResolver.java:225)
DEBUG:     at soot.SootResolver.bringToSignatures(SootResolver.java:298)
DEBUG:     at soot.SootResolver.bringToBodies(SootResolver.java:342)
DEBUG:     at soot.SootResolver.processResolveWorklist(SootResolver.java:171)
DEBUG:     at soot.SootResolver.resolveClass(SootResolver.java:135)
DEBUG:     at soot.Scene.loadClass(Scene.java:492)
DEBUG:     at soot.Scene.loadClassAndSupport(Scene.java:479)
DEBUG:     at soot.Scene.loadNecessaryClasses(Scene.java:1331)

This is due to computeIfAbsent being called recursively, triggering a reentrant guard in ConcurrentHashMap.

I've rewritten the ArrayTypeCache as such and found it works nicely, speeding up some of our processing by 25%:

public class ArrayTypeCache {
  private final Map<Pair<Type, Integer>, ArrayType> cache = new ConcurrentHashMap<>();

  public ArrayTypeCache(final Global g) {
  }

  /**
   * Returns a potentially cached array type of the given dimensions
   * @param baseType the base type (array element type)
   * @param dimensionsCurrent the number of dimensions
   * @return the array type
   */
  public ArrayType getArrayType(final Type baseType, final int dimensionsCurrent) {
    final Pair<Type, Integer> pairSearch = new Pair<>(baseType, dimensionsCurrent);
    final ArrayType result = cache.get(pairSearch);
    if (result != null) {
      return result;
    }

    Type elementType = baseType;
    for (int i = 1; i <= dimensionsCurrent; i++) {
      final ArrayType ret =
        cache.computeIfAbsent( new Pair<>(baseType, i),
                               k -> { return new ArrayType(k.getO1(), k.getO2()); });
      elementType.setArrayType(ret);
      elementType = ret;
    }

    return cache.get(pairSearch);
  }
}