1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17 package libcore.tools.analyzer.openjdk; 18 19 import org.objectweb.asm.Handle; 20 import org.objectweb.asm.Type; 21 import org.objectweb.asm.tree.ClassNode; 22 import org.objectweb.asm.tree.FieldInsnNode; 23 import org.objectweb.asm.tree.FieldNode; 24 import org.objectweb.asm.tree.InvokeDynamicInsnNode; 25 import org.objectweb.asm.tree.MethodInsnNode; 26 import org.objectweb.asm.tree.MethodNode; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.LinkedHashSet; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.function.Predicate; 35 import java.util.stream.Stream; 36 37 /** 38 * Collector of classes, fields and methods. 39 */ 40 public class SignaturesCollector { 41 42 private final List<String> mClasses = new ArrayList<>(); 43 private final List<Field> mFields = new ArrayList<>(); 44 private final List<Method> mMethods = new ArrayList<>(); 45 private Predicate<String> mClassPredicate = (c) -> true; 46 private Predicate<Field> mFieldPredicate = (f) -> true; 47 private Predicate<Method> mMethodPredicate = (m) -> true; 48 SignaturesCollector()49 public SignaturesCollector() {} 50 add(MethodInsnNode node)51 public SignaturesCollector add(MethodInsnNode node) { 52 return add(new Method(node)); 53 } 54 add(InvokeDynamicInsnNode node)55 public SignaturesCollector add(InvokeDynamicInsnNode node) { 56 add(new Method(node.bsm)); 57 for (Object obj : node.bsmArgs) { 58 if (obj instanceof Handle) { 59 add(new Method((Handle) obj)); 60 } 61 } 62 return this; 63 } 64 add(Method method)65 private SignaturesCollector add(Method method) { 66 if (mMethodPredicate.test(method)) { 67 mMethods.add(method); 68 } 69 return this; 70 } add(String internalClassName, MethodNode methodNode)71 public SignaturesCollector add(String internalClassName, MethodNode methodNode) { 72 Method method = new Method(internalClassName, methodNode); 73 return add(method); 74 } 75 add(FieldInsnNode node)76 public SignaturesCollector add(FieldInsnNode node) { 77 return add(new Field(node)); 78 } 79 add(String internalClassName, FieldNode fieldNode)80 public SignaturesCollector add(String internalClassName, FieldNode fieldNode) { 81 Field field = new Field(internalClassName, fieldNode); 82 return add(field); 83 } 84 85 add(Field field)86 private SignaturesCollector add(Field field) { 87 if (mFieldPredicate.test(field)) { 88 mFields.add(field); 89 } 90 return this; 91 } 92 add(SignaturesCollection collection)93 public SignaturesCollector add(SignaturesCollection collection) { 94 collection.mClasses.stream() 95 .filter(mClassPredicate) 96 .forEach(this::addClass); 97 collection.mFields.stream() 98 .filter(mFieldPredicate) 99 .forEach(this::add); 100 collection.mMethods.stream() 101 .filter(mMethodPredicate) 102 .forEach(this::add); 103 return this; 104 } 105 106 /** 107 * A primitive type is ignored. 108 * 109 * @param internalOrDesc internal name or type descriptor 110 */ addClass(String internalOrDesc)111 public SignaturesCollector addClass(String internalOrDesc) { 112 String internalClassName = getInternalName(internalOrDesc); 113 if (internalClassName != null && mClassPredicate.test(internalClassName)) { 114 mClasses.add(internalClassName); 115 } 116 return this; 117 } 118 119 /** 120 * Add all non-primitive types of fields, method arguments, method returns, and parent classes 121 * of the {@link ClassNode} into the collection. 122 */ addClassesFromClassNode(ClassNode node)123 public SignaturesCollector addClassesFromClassNode(ClassNode node) { 124 addClass(node.superName); 125 node.interfaces.forEach(this::addClass); 126 node.fields.forEach(fieldNode -> addClass(Type.getType(fieldNode.desc).getInternalName())); 127 128 List<MethodNode> methods = new ArrayList<>(node.methods); 129 for (MethodNode method : methods) { 130 addClass(Type.getReturnType(method.desc).getInternalName()); 131 Arrays.stream(Type.getArgumentTypes(method.desc)) 132 .forEach(type -> addClass(type.getInternalName())); 133 } 134 return this; 135 } 136 getInternalName(String internalOrDesc)137 private static String getInternalName(String internalOrDesc) { 138 if (internalOrDesc == null || internalOrDesc.isEmpty()) { 139 return null; 140 } 141 // Return null if it's a primitive type. 142 if (internalOrDesc.length() == 1 && "VZCBSIFJD".contains(internalOrDesc)) { 143 return null; 144 } 145 Type type = Type.getObjectType(internalOrDesc); 146 if (type.getSort() == Type.ARRAY) { 147 type = type.getElementType(); 148 } 149 if (type.getSort() != Type.OBJECT) { 150 // return null for primitive types. 151 return null; 152 } 153 154 return type.getInternalName(); 155 } 156 setClassFilter(Predicate<String> predicate)157 public SignaturesCollector setClassFilter(Predicate<String> predicate) { 158 this.mClassPredicate = predicate; 159 return this; 160 } 161 setFieldFilter(Predicate<Field> predicate)162 public SignaturesCollector setFieldFilter(Predicate<Field> predicate) { 163 this.mFieldPredicate = predicate; 164 return this; 165 } 166 setMethodFilter(Predicate<Method> predicate)167 public SignaturesCollector setMethodFilter(Predicate<Method> predicate) { 168 this.mMethodPredicate = predicate; 169 return this; 170 } getCollection()171 public SignaturesCollection getCollection() { 172 Collections.sort(mClasses); 173 Collections.sort(mFields); 174 Collections.sort(mMethods); 175 return new SignaturesCollection(mClasses, mFields, mMethods); 176 } 177 178 public static class SignaturesCollection { 179 private final LinkedHashSet<String> mClasses; 180 private final LinkedHashSet<Field> mFields; 181 182 private final LinkedHashSet<Method> mMethods; 183 SignaturesCollection(List<String> classes, List<Field> fields, List<Method> methods)184 private SignaturesCollection(List<String> classes, List<Field> fields, 185 List<Method> methods) { 186 mClasses = new LinkedHashSet<>(classes); 187 mFields = new LinkedHashSet<>(fields); 188 mMethods = new LinkedHashSet<>(methods); 189 } 190 getClassStream()191 public Stream<String> getClassStream() { 192 return mClasses.stream(); 193 } getFieldStream()194 public Stream<Field> getFieldStream() { 195 return mFields.stream(); 196 } 197 getMethodStream()198 public Stream<Method> getMethodStream() { 199 return mMethods.stream(); 200 } 201 containsClass(String internalClassName)202 public boolean containsClass(String internalClassName) { 203 return mClasses.contains(internalClassName); 204 } 205 contains(String internalClassName, MethodNode node)206 public boolean contains(String internalClassName, MethodNode node) { 207 return contains(new Method(internalClassName, node)); 208 } 209 contains(MethodInsnNode node)210 public boolean contains(MethodInsnNode node) { 211 return contains(new Method(node)); 212 } 213 contains(Handle handle)214 public boolean contains(Handle handle) { 215 return contains(new Method(handle)); 216 } 217 contains(FieldInsnNode node)218 public boolean contains(FieldInsnNode node) { 219 return contains(new Field(node)); 220 } 221 contains(Method method)222 private boolean contains(Method method) { 223 return mMethods.contains(method); 224 } 225 contains(Field field)226 private boolean contains(Field field) { 227 return mFields.contains(field); 228 } 229 contains(MemberInfo info)230 public boolean contains(MemberInfo info) { 231 if (info instanceof Method) { 232 return contains(((Method) info)); 233 } else { 234 return contains(((Field) info)); 235 } 236 } 237 containsField(String owner, String name, String desc)238 public boolean containsField(String owner, String name, String desc) { 239 return contains(new Field(owner, name, desc)); 240 } 241 containsMethod(String owner, String name, String desc)242 public boolean containsMethod(String owner, String name, String desc) { 243 return contains(new Method(owner, name, desc)); 244 } 245 isEmpty()246 public boolean isEmpty() { 247 return mClasses.isEmpty() && mFields.isEmpty() && mMethods.isEmpty(); 248 } 249 } 250 251 public interface MemberInfo { getOwner()252 String getOwner(); getName()253 String getName(); getDesc()254 String getDesc(); 255 } 256 257 public static class Field implements Comparable<Field>, MemberInfo { 258 /** 259 * Internal class name using `/` separator 260 */ 261 private final String owner; 262 private final String name; 263 /** 264 * The type descriptor of the field. 265 * 266 * @see Type#getDescriptor() 267 */ 268 private final String desc; 269 Field(String owner, String name, String desc)270 private Field(String owner, String name, String desc) { 271 this.owner = owner; 272 this.name = name; 273 this.desc = desc; 274 } 275 Field(FieldInsnNode node)276 private Field(FieldInsnNode node) { 277 this(node.owner, node.name, node.desc); 278 } 279 Field(String internalClassName, FieldNode fieldNode)280 private Field(String internalClassName, FieldNode fieldNode) { 281 this(internalClassName, fieldNode.name, fieldNode.desc); 282 } 283 284 @Override equals(Object o)285 public boolean equals(Object o) { 286 if (this == o) return true; 287 if (!(o instanceof Field)) return false; 288 Field field = (Field) o; 289 return owner.equals(field.owner) && name.equals(field.name) && desc.equals(field.desc); 290 } 291 292 @Override hashCode()293 public int hashCode() { 294 return Objects.hash(owner, name, desc); 295 } 296 297 @Override toString()298 public String toString() { 299 return owner + "." + name + " : " + desc; 300 } 301 302 @Override compareTo(Field f)303 public int compareTo(Field f) { 304 return toString().compareTo(f.toString()); 305 } 306 307 @Override getOwner()308 public String getOwner() { 309 return owner; 310 } 311 312 @Override getName()313 public String getName() { 314 return name; 315 } 316 317 @Override getDesc()318 public String getDesc() { 319 return desc; 320 } 321 } 322 323 public static class Method implements Comparable<Method>, MemberInfo { 324 /** 325 * Internal class name using `/` separator 326 */ 327 private final String owner; 328 private final String name; 329 330 /** 331 * The method descriptor. 332 * 333 * @see MethodNode#desc 334 * @see Type#getMethodDescriptor(Type, Type...) 335 */ 336 private final String desc; 337 Method(String owner, String name, String desc)338 private Method(String owner, String name, String desc) { 339 this.owner = owner; 340 this.name = name; 341 this.desc = desc; 342 } 343 Method(MethodInsnNode node)344 private Method(MethodInsnNode node) { 345 this(node.owner, node.name, node.desc); 346 } 347 Method(String internalClassName, MethodNode node)348 private Method(String internalClassName, MethodNode node) { 349 this(internalClassName, node.name, node.desc); 350 } 351 Method(Handle handle)352 private Method(Handle handle) { 353 this(handle.getOwner(), handle.getName(), handle.getDesc()); 354 } 355 356 @Override equals(Object o)357 public boolean equals(Object o) { 358 if (this == o) return true; 359 if (!(o instanceof Method)) return false; 360 Method method = (Method) o; 361 return owner.equals(method.owner) && name.equals(method.name) && desc.equals( 362 method.desc); 363 } 364 365 @Override hashCode()366 public int hashCode() { 367 return Objects.hash(owner, name, desc); 368 } 369 370 @Override toString()371 public String toString() { 372 return owner + "#" + name + desc; 373 } 374 375 @Override compareTo(Method m)376 public int compareTo(Method m) { 377 return toString().compareTo(m.toString()); 378 } 379 380 @Override getOwner()381 public String getOwner() { 382 return owner; 383 } 384 385 @Override getName()386 public String getName() { 387 return name; 388 } 389 390 @Override getDesc()391 public String getDesc() { 392 return desc; 393 } 394 } 395 } 396