• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.utils;
5 
6 import com.android.tools.r8.graph.DexEncodedMethod;
7 import com.android.tools.r8.utils.DexInspector.ClassSubject;
8 import com.android.tools.r8.utils.DexInspector.MethodSubject;
9 import java.util.ArrayList;
10 import java.util.List;
11 
12 public class ArtErrorParser {
13   private static final String VERIFICATION_ERROR_HEADER = "Verification error in ";
14   private static final String METHOD_EXCEEDS_INSTRUCITON_LIMIT =
15       "Method exceeds compiler instruction limit: ";
16 
17   public static class ArtErrorParserException extends Exception {
ArtErrorParserException(String message)18     public ArtErrorParserException(String message) {
19       super(message);
20     }
21   }
22 
23   private static class ParseInput {
24     public final String input;
25     public int pos = 0;
26 
ParseInput(String input)27     public ParseInput(String input) {
28       this.input = input;
29     }
30 
hasNext()31     public boolean hasNext() {
32       return pos < input.length();
33     }
34 
next()35     public char next() {
36       assert hasNext();
37       return input.charAt(pos++);
38     }
39 
peek()40     public char peek() {
41       return input.charAt(pos);
42     }
43 
whitespace()44     public void whitespace() {
45       while (peek() == ' ') {
46         next();
47       }
48     }
49 
until(int end)50     public String until(int end) {
51       String result = input.substring(pos, end);
52       pos = end;
53       return result;
54     }
55 
until(char match)56     public String until(char match) throws ArtErrorParserException {
57       int end = input.indexOf(match, pos);
58       if (end < 0) {
59         throw new ArtErrorParserException("Expected to find " + match + " at " + pos);
60       }
61       return until(end);
62     }
63 
check(char expected)64     public void check(char expected) throws ArtErrorParserException {
65       if (peek() != expected) {
66         throw new ArtErrorParserException("Expected " + expected + " found " + pos);
67       }
68     }
69   }
70 
71   public static abstract class ArtErrorInfo {
consumedLines()72     public abstract int consumedLines();
getMessage()73     public abstract String getMessage();
dump(DexInspector inspector, boolean markLocation)74     public abstract String dump(DexInspector inspector, boolean markLocation);
75   }
76 
77   private static class ArtMethodError extends ArtErrorInfo {
78     public final String className;
79     public final String methodName;
80     public final String methodReturn;
81     public final List<String> methodFormals;
82     public final String methodSignature;
83     public final String errorMessage;
84     public final List<Long> locations;
85     private final int consumedLines;
86 
ArtMethodError(String className, String methodName, String methodReturn, List<String> methodFormals, String methodSignature, String errorMessage, List<Long> locations, int consumedLines)87     public ArtMethodError(String className,
88         String methodName, String methodReturn, List<String> methodFormals,
89         String methodSignature, String errorMessage,
90         List<Long> locations,
91         int consumedLines) {
92       this.className = className;
93       this.methodName = methodName;
94       this.methodReturn = methodReturn;
95       this.methodFormals = methodFormals;
96       this.methodSignature = methodSignature;
97       this.errorMessage = errorMessage;
98       this.locations = locations;
99       this.consumedLines = consumedLines;
100     }
101 
102     @Override
consumedLines()103     public int consumedLines() {
104       return consumedLines;
105     }
106 
107     @Override
getMessage()108     public String getMessage() {
109       StringBuilder builder = new StringBuilder();
110       builder.append("\n")
111           .append(VERIFICATION_ERROR_HEADER)
112           .append(methodSignature)
113           .append(":\n")
114           .append(errorMessage);
115       return builder.toString();
116     }
117 
118     @Override
dump(DexInspector inspector, boolean markLocation)119     public String dump(DexInspector inspector, boolean markLocation) {
120       ClassSubject clazz = inspector.clazz(className);
121       MethodSubject method = clazz.method(methodReturn, methodName, methodFormals);
122       DexEncodedMethod dex = method.getMethod();
123       if (dex == null) {
124         StringBuilder builder = new StringBuilder("Failed to lookup method: ");
125         builder.append(className).append(".").append(methodName);
126         StringUtils.append(builder, methodFormals);
127         return builder.toString();
128       }
129 
130       String code = method.getMethod().codeToString();
131       if (markLocation && !locations.isEmpty()) {
132         for (Long location : locations) {
133           String locationString = "" + location + ":";
134           code = code.replaceFirst(":(\\s*) " + locationString, ":$1*" + locationString);
135         }
136       }
137       return code;
138     }
139   }
140 
parse(String message)141   public static List<ArtErrorInfo> parse(String message) throws ArtErrorParserException {
142     List<ArtErrorInfo> errors = new ArrayList<>();
143     String[] lines = message.split("\n");
144     for (int i = 0; i < lines.length; i++) {
145       ArtErrorInfo error = parse(lines, i);
146       if (error != null) {
147         errors.add(error);
148         i += error.consumedLines();
149       }
150     }
151     return errors;
152   }
153 
parse(String[] lines, final int line)154   private static ArtErrorInfo parse(String[] lines, final int line) throws ArtErrorParserException {
155     String methodSig = null;
156     StringBuilder errorMessageContent = new StringBuilder();
157     int currentLine = line;
158     {
159       int index = lines[currentLine].indexOf(VERIFICATION_ERROR_HEADER);
160       if (index >= 0) {
161         // Read/skip the header line.
162         String lineContent = lines[currentLine++].substring(index);
163         // Append the content of each subsequent line that can be parsed as an "error message".
164         for (; currentLine < lines.length; ++currentLine) {
165           lineContent = lines[currentLine].substring(index);
166           String[] parts = lineContent.split(":");
167           if (parts.length == 2) {
168             if (methodSig == null) {
169               methodSig = parts[0];
170               errorMessageContent.append(parts[1]);
171             } else if (methodSig.equals(parts[0])) {
172               errorMessageContent.append(parts[1]);
173             } else {
174               break;
175             }
176           } else if (parts.length >= 3) {
177             if (methodSig == null) {
178               methodSig = parts[1];
179               for (int i = 2; i < parts.length; ++i) {
180                 errorMessageContent.append(parts[i]);
181               }
182             } else if (methodSig.equals(parts[1])) {
183               for (int i = 2; i < parts.length; ++i) {
184                 errorMessageContent.append(parts[i]);
185               }
186             } else {
187               break;
188             }
189           } else {
190             break;
191           }
192         }
193         if (methodSig == null) {
194           throw new ArtErrorParserException("Unexpected art error message: " + lineContent);
195         }
196       }
197     }
198     if (methodSig == null) {
199       int index = lines[currentLine].indexOf(METHOD_EXCEEDS_INSTRUCITON_LIMIT);
200       if (index >= 0) {
201         String lineContent = lines[currentLine++].substring(index);
202         String[] parts = lineContent.split(":");
203         if (parts.length == 2) {
204           errorMessageContent.append(lineContent);
205           methodSig = parts[1].substring(parts[1].indexOf(" in ") + 4);
206         } else {
207           throw new ArtErrorParserException("Unexpected art error message parts: " + parts);
208         }
209       }
210     }
211 
212     // Return if we failed to identify an error description.
213     if (methodSig == null) {
214       return null;
215     }
216 
217     String errorMessage = errorMessageContent.toString();
218     ParseInput input = new ParseInput(methodSig);
219     String methodReturn = parseType(input);
220     String[] qualifiedName = parseQualifiedName(input);
221     List<String> methodFormals = parseTypeList(input);
222     List<Long> locations = parseLocations(errorMessage);
223     return new ArtMethodError(getClassName(qualifiedName), getMethodName(qualifiedName),
224         methodReturn, methodFormals, methodSig, errorMessage, locations, currentLine - line);
225   }
226 
getClassName(String[] parts)227   private static String getClassName(String[] parts) {
228     StringBuilder builder = new StringBuilder();
229     for (int i = 0; i < parts.length - 1; i++) {
230       if (i != 0) {
231         builder.append('.');
232       }
233       builder.append(parts[i]);
234     }
235     return builder.toString();
236   }
237 
getMethodName(String[] parts)238   private static String getMethodName(String[] parts) {
239     return parts[parts.length - 1];
240   }
241 
parseType(ParseInput input)242   private static String parseType(ParseInput input) throws ArtErrorParserException {
243     input.whitespace();
244     String type = input.until(' ');
245     assert !type.contains("<");
246     input.whitespace();
247     return type;
248   }
249 
parseQualifiedName(ParseInput input)250   private static String[] parseQualifiedName(ParseInput input) throws ArtErrorParserException {
251     input.whitespace();
252     return input.until('(').split("\\.");
253   }
254 
parseTypeList(ParseInput input)255   private static List<String> parseTypeList(ParseInput input) throws ArtErrorParserException {
256     input.check('(');
257     input.next();
258     String content = input.until(')').trim();
259     if (content.isEmpty()) {
260       return new ArrayList<>();
261     }
262     String[] rawList = content.split(",");
263     List<String> types = new ArrayList<>();
264     for (String type : rawList) {
265       types.add(type.trim());
266     }
267     input.check(')');
268     input.next();
269     return types;
270   }
271 
isHexChar(char c)272   private static boolean isHexChar(char c) {
273     return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F';
274   }
275 
parseLocations(String input)276   private static List<Long> parseLocations(String input) {
277     List<Long> locations = new ArrayList<>();
278     int length = input.length();
279     int start = 0;
280     while (start < length && (start = input.indexOf("0x", start)) >= 0) {
281       int end = start + 2;
282       while (end < length && isHexChar(input.charAt(end))) {
283         ++end;
284       }
285       long l = Long.parseLong(input.substring(start + 2, end), 16);
286       if (l >= 0) {
287         locations.add(l);
288       }
289       start = end;
290     }
291     return locations;
292   }
293 }
294