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