# Find invokeinterface calls to java.lang.Object methods (toString/equals/hashCode/getClass) through an # interface that doesn't declare them itself: Dalvik before 4.0 can't resolve those (NoSuchMethodError). import os, re, subprocess, sys, zipfile classes = sys.argv[1]; android_jar = sys.argv[2] OBJ = {"toString:()Ljava/lang/String;", "equals:(Ljava/lang/Object;)Z", "hashCode:()I", "getClass:()Ljava/lang/Class;"} z = zipfile.ZipFile(android_jar) declared = {} def declares(iface, m): key = (iface, m) if key in declared: return declared[key] path = os.path.join(classes, iface + ".class") src = path if os.path.exists(path) else None args = ["javap", "-p", "-s", "-cp", classes if src else android_jar, iface.replace("/", ".")] out = subprocess.run(args, capture_output=True, text=True).stdout name, desc = m.split(":") found = False lines = out.splitlines() for i, l in enumerate(lines): if re.search(r"\b" + name + r"\(", l) and i + 1 < len(lines) and desc in lines[i + 1]: found = True # superinterfaces declaring it also count if not found: mm = re.search(r"interface \S+ extends (.+?) \{", out) if mm: for sup in mm.group(1).split(","): sup = sup.strip().split("<")[0].replace(".", "/") if sup and declares(sup, m): found = True declared[key] = found return found hits = {} files = [os.path.join(dp, f) for dp, _, fs in os.walk(classes) for f in fs if f.endswith(".class")] for i in range(0, len(files), 200): out = subprocess.run(["javap", "-c", "-p"] + files[i:i+200], capture_output=True, text=True).stdout cur = None for line in out.splitlines(): m = re.match(r"^(?:public |final |abstract |class |interface )*.*?(?:class|interface) (\S+)", line) if m and not line.startswith(" "): cur = m.group(1) mi = re.search(r"invokeinterface\s+#\d+,\s*\d+\s*// InterfaceMethod (\S+)\.(\w+):(\S+)", line) if mi: iface, name, desc = mi.group(1), mi.group(2), mi.group(3) sig = name + ":" + desc if sig in OBJ and not declares(iface, sig): hits.setdefault((iface, sig), set()).add(cur) for (iface, sig), users in sorted(hits.items()): print(f"{iface}.{sig} <- {len(users)} class(es): " + ", ".join(sorted(users))[:300]) print("total patterns:", len(hits))