blob: 6a4f5e5a2633e022ff275dce42da86542d0d24f6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.tools.example.debug.bdi;
27
28import com.sun.jdi.*;
29import com.sun.jdi.request.*;
30
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Iterator;
34
35public class MethodBreakpointSpec extends BreakpointSpec {
36 String methodId;
37 List methodArgs;
38
39 MethodBreakpointSpec(EventRequestSpecList specs,
40 ReferenceTypeSpec refSpec,
41 String methodId, List methodArgs) {
42 super(specs, refSpec);
43 this.methodId = methodId;
44 this.methodArgs = methodArgs;
45 }
46
47 /**
48 * The 'refType' is known to match.
49 */
50 void resolve(ReferenceType refType) throws MalformedMemberNameException,
51 AmbiguousMethodException,
52 InvalidTypeException,
53 NoSuchMethodException,
54 NoSessionException {
55 if (!isValidMethodName(methodId)) {
56 throw new MalformedMemberNameException(methodId);
57 }
58 if (!(refType instanceof ClassType)) {
59 throw new InvalidTypeException();
60 }
61 Location location = location((ClassType)refType);
62 setRequest(refType.virtualMachine().eventRequestManager()
63 .createBreakpointRequest(location));
64 }
65
66 private Location location(ClassType clazz) throws
67 AmbiguousMethodException,
68 NoSuchMethodException,
69 NoSessionException {
70 Method method = findMatchingMethod(clazz);
71 Location location = method.location();
72 return location;
73 }
74
75 public String methodName() {
76 return methodId;
77 }
78
79 public List methodArgs() {
80 return methodArgs;
81 }
82
83 public int hashCode() {
84 return refSpec.hashCode() +
85 ((methodId != null) ? methodId.hashCode() : 0) +
86 ((methodArgs != null) ? methodArgs.hashCode() : 0);
87 }
88
89 public boolean equals(Object obj) {
90 if (obj instanceof MethodBreakpointSpec) {
91 MethodBreakpointSpec breakpoint = (MethodBreakpointSpec)obj;
92
93 return methodId.equals(breakpoint.methodId) &&
94 methodArgs.equals(breakpoint.methodArgs) &&
95 refSpec.equals(breakpoint.refSpec);
96 } else {
97 return false;
98 }
99 }
100
101 public String errorMessageFor(Exception e) {
102 if (e instanceof AmbiguousMethodException) {
103 return ("Method " + methodName() + " is overloaded; specify arguments");
104 /*
105 * TO DO: list the methods here
106 */
107 } else if (e instanceof NoSuchMethodException) {
108 return ("No method " + methodName() + " in " + refSpec);
109 } else if (e instanceof InvalidTypeException) {
110 return ("Breakpoints can be located only in classes. " +
111 refSpec + " is an interface or array");
112 } else {
113 return super.errorMessageFor( e);
114 }
115 }
116
117 public String toString() {
118 StringBuffer buffer = new StringBuffer("breakpoint ");
119 buffer.append(refSpec.toString());
120 buffer.append('.');
121 buffer.append(methodId);
122 if (methodArgs != null) {
123 Iterator iter = methodArgs.iterator();
124 boolean first = true;
125 buffer.append('(');
126 while (iter.hasNext()) {
127 if (!first) {
128 buffer.append(',');
129 }
130 buffer.append((String)iter.next());
131 first = false;
132 }
133 buffer.append(")");
134 }
135 buffer.append(" (");
136 buffer.append(getStatusString());
137 buffer.append(')');
138 return buffer.toString();
139 }
140
141 private boolean isValidMethodName(String s) {
142 return isJavaIdentifier(s) ||
143 s.equals("<init>") ||
144 s.equals("<clinit>");
145 }
146
147 /*
148 * Compare a method's argument types with a Vector of type names.
149 * Return true if each argument type has a name identical to the
150 * corresponding string in the vector (allowing for varargs)
151 * and if the number of arguments in the method matches the
152 * number of names passed
153 */
154 private boolean compareArgTypes(Method method, List nameList) {
155 List argTypeNames = method.argumentTypeNames();
156
157 // If argument counts differ, we can stop here
158 if (argTypeNames.size() != nameList.size()) {
159 return false;
160 }
161
162 // Compare each argument type's name
163 int nTypes = argTypeNames.size();
164 for (int i = 0; i < nTypes; ++i) {
165 String comp1 = (String)argTypeNames.get(i);
166 String comp2 = (String)nameList.get(i);
167 if (! comp1.equals(comp2)) {
168 /*
169 * We have to handle varargs. EG, the
170 * method's last arg type is xxx[]
171 * while the nameList contains xxx...
172 * Note that the nameList can also contain
173 * xxx[] in which case we don't get here.
174 */
175 if (i != nTypes - 1 ||
176 !method.isVarArgs() ||
177 !comp2.endsWith("...")) {
178 return false;
179 }
180 /*
181 * The last types differ, it is a varargs
182 * method and the nameList item is varargs.
183 * We just have to compare the type names, eg,
184 * make sure we don't have xxx[] for the method
185 * arg type and yyy... for the nameList item.
186 */
187 int comp1Length = comp1.length();
188 if (comp1Length + 1 != comp2.length()) {
189 // The type names are different lengths
190 return false;
191 }
192 // We know the two type names are the same length
193 if (!comp1.regionMatches(0, comp2, 0, comp1Length - 2)) {
194 return false;
195 }
196 // We do have xxx[] and xxx... as the last param type
197 return true;
198 }
199 }
200
201 return true;
202 }
203
204 private VirtualMachine vm() {
205 return request.virtualMachine();
206 }
207
208 /**
209 * Remove unneeded spaces and expand class names to fully
210 * qualified names, if necessary and possible.
211 */
212 private String normalizeArgTypeName(String name) throws NoSessionException {
213 /*
214 * Separate the type name from any array modifiers,
215 * stripping whitespace after the name ends.
216 */
217 int i = 0;
218 StringBuffer typePart = new StringBuffer();
219 StringBuffer arrayPart = new StringBuffer();
220 name = name.trim();
221 int nameLength = name.length();
222 /*
223 * For varargs, there can be spaces before the ... but not
224 * within the ... So, we will just ignore the ...
225 * while stripping blanks.
226 */
227 boolean isVarArgs = name.endsWith("...");
228 if (isVarArgs) {
229 nameLength -= 3;
230 }
231
232 while (i < nameLength) {
233 char c = name.charAt(i);
234 if (Character.isWhitespace(c) || c == '[') {
235 break; // name is complete
236 }
237 typePart.append(c);
238 i++;
239 }
240 while (i < nameLength) {
241 char c = name.charAt(i);
242 if ( (c == '[') || (c == ']')) {
243 arrayPart.append(c);
244 } else if (!Character.isWhitespace(c)) {
245 throw new IllegalArgumentException(
246 "Invalid argument type name");
247
248 }
249 i++;
250 }
251
252 name = typePart.toString();
253
254 /*
255 * When there's no sign of a package name already,
256 * try to expand the
257 * the name to a fully qualified class name
258 */
259 if ((name.indexOf('.') == -1) || name.startsWith("*.")) {
260 try {
261 List refs = specs.runtime.findClassesMatchingPattern(name);
262 if (refs.size() > 0) { //### ambiguity???
263 name = ((ReferenceType)(refs.get(0))).name();
264 }
265 } catch (IllegalArgumentException e) {
266 // We'll try the name as is
267 }
268 }
269 name += arrayPart.toString();
270 if (isVarArgs) {
271 name += "...";
272 }
273 return name;
274 }
275
276 /*
277 * Attempt an unambiguous match of the method name and
278 * argument specification to a method. If no arguments
279 * are specified, the method must not be overloaded.
280 * Otherwise, the argument types much match exactly
281 */
282 private Method findMatchingMethod(ClassType clazz)
283 throws AmbiguousMethodException,
284 NoSuchMethodException,
285 NoSessionException {
286
287 // Normalize the argument string once before looping below.
288 List<String> argTypeNames = null;
289 if (methodArgs() != null) {
290 argTypeNames = new ArrayList<String>(methodArgs().size());
291 Iterator iter = methodArgs().iterator();
292 while (iter.hasNext()) {
293 String name = (String)iter.next();
294 name = normalizeArgTypeName(name);
295 argTypeNames.add(name);
296 }
297 }
298
299 // Check each method in the class for matches
300 Iterator iter = clazz.methods().iterator();
301 Method firstMatch = null; // first method with matching name
302 Method exactMatch = null; // (only) method with same name & sig
303 int matchCount = 0; // > 1 implies overload
304 while (iter.hasNext()) {
305 Method candidate = (Method)iter.next();
306
307 if (candidate.name().equals(methodName())) {
308 matchCount++;
309
310 // Remember the first match in case it is the only one
311 if (matchCount == 1) {
312 firstMatch = candidate;
313 }
314
315 // If argument types were specified, check against candidate
316 if ((argTypeNames != null)
317 && compareArgTypes(candidate, argTypeNames) == true) {
318 exactMatch = candidate;
319 break;
320 }
321 }
322 }
323
324 // Determine method for breakpoint
325 Method method = null;
326 if (exactMatch != null) {
327 // Name and signature match
328 method = exactMatch;
329 } else if ((argTypeNames == null) && (matchCount > 0)) {
330 // At least one name matched and no arg types were specified
331 if (matchCount == 1) {
332 method = firstMatch; // Only one match; safe to use it
333 } else {
334 throw new AmbiguousMethodException();
335 }
336 } else {
337 throw new NoSuchMethodException(methodName());
338 }
339 return method;
340 }
341}