1 /*
2  * Copyright (C) 2018 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 package android.signature.cts;
17 
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 import java.util.stream.Collectors;
23 
24 public class DexMethod extends DexMember {
25   private final List<String> mParamTypeList;
26 
27   private enum ParseType {
28       DEX_TYPE_LIST,
29       DEX_RETURN_TYPE,
30   }
31 
DexMethod(String className, String name, String signature, String[] flags)32   public DexMethod(String className, String name, String signature, String[] flags) {
33       super(className, name, parseSignature(signature, ParseType.DEX_RETURN_TYPE), flags);
34       mParamTypeList = parseDexTypeList(signature);
35   }
36 
getDexSignature()37   public String getDexSignature() {
38       return "(" + String.join("", mParamTypeList) + ")" + getDexType();
39   }
40 
getJavaParameterTypes()41   public List<String> getJavaParameterTypes() {
42       return mParamTypeList.stream().map(DexMember::dexToJavaType).collect(Collectors.toList());
43   }
44 
getJavaParameterClasses()45   public Class<?>[] getJavaParameterClasses() throws ClassNotFoundException {
46     // Ideally we'd use streams, but DexMember.typeToClass throws a checked exception, and that's
47     // tricky to handle.
48     Class<?>[] classes = new Class<?>[mParamTypeList.size()];
49     int i = 0;
50     for (String param : mParamTypeList) {
51         classes[i++] = DexMember.typeToClass(param);
52     }
53     return classes;
54   }
55 
isConstructor()56   public boolean isConstructor() {
57       return "<init>".equals(getName()) && "V".equals(getDexType());
58   }
59 
isStaticConstructor()60   public boolean isStaticConstructor() {
61       return "<clinit>".equals(getName()) && "V".equals(getDexType());
62   }
63 
64   @Override
toString()65   public String toString() {
66       return getJavaType() + " " + getJavaClassName() + "." + getName()
67               + "(" + String.join(", ", getJavaParameterTypes()) + ")";
68   }
69 
parseSignature(String signature, ParseType type)70   private static String parseSignature(String signature, ParseType type) {
71       // Form of signature:
72       //   (DexTypeList)DexReturnType
73       int length = signature.length();
74       int paren = signature.indexOf(')', 1);
75       if (length < 2 || signature.charAt(0) != '(' || paren == -1) {
76           throw new RuntimeException("Could not parse method signature: " + signature);
77       }
78       if (type == ParseType.DEX_TYPE_LIST) {
79           return signature.substring(1, paren);
80       } else {
81           // ParseType.DEX_RETURN_TYPE
82           return signature.substring(paren + 1, length);
83       }
84   }
85 
parseDexTypeList(String signature)86   private static List<String> parseDexTypeList(String signature) {
87       String typeSequence = parseSignature(signature, ParseType.DEX_TYPE_LIST);
88       List<String> list = new ArrayList<String>();
89       while (!typeSequence.isEmpty()) {
90           String type = firstDexTypeFromList(typeSequence);
91           list.add(type);
92           typeSequence = typeSequence.substring(type.length());
93       }
94       return list;
95   }
96 
97   /**
98    * Returns the first dex type in `typeList` or throws a ParserException
99    * if a dex type is not recognized. The input is not changed.
100    */
firstDexTypeFromList(String typeList)101   private static String firstDexTypeFromList(String typeList) {
102       String dexDimension = "";
103       while (typeList.startsWith("[")) {
104           dexDimension += "[";
105           typeList = typeList.substring(1);
106       }
107 
108       String type = null;
109       if (typeList.startsWith("V")
110               || typeList.startsWith("Z")
111               || typeList.startsWith("B")
112               || typeList.startsWith("C")
113               || typeList.startsWith("S")
114               || typeList.startsWith("I")
115               || typeList.startsWith("J")
116               || typeList.startsWith("F")
117               || typeList.startsWith("D")) {
118           type = typeList.substring(0, 1);
119       } else if (typeList.startsWith("L") && typeList.indexOf(";") > 0) {
120           type = typeList.substring(0, typeList.indexOf(";") + 1);
121       } else {
122           throw new RuntimeException("Unexpected dex type in \"" + typeList + "\"");
123       }
124 
125       return dexDimension + type;
126   }
127 }
128