|
1 /* |
|
2 * Copyright 2000-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 |
|
26 package com.sun.tools.jdi; |
|
27 |
|
28 import com.sun.jdi.*; |
|
29 |
|
30 import java.util.List; |
|
31 import java.util.Map; |
|
32 import java.util.Iterator; |
|
33 import java.util.ListIterator; |
|
34 import java.util.HashMap; |
|
35 import java.util.ArrayList; |
|
36 import java.util.Collections; |
|
37 import java.lang.ref.SoftReference; |
|
38 |
|
39 /** |
|
40 * Represents methods with method bodies. |
|
41 * That is, non-native non-abstract methods. |
|
42 * Private to MethodImpl. |
|
43 */ |
|
44 public class ConcreteMethodImpl extends MethodImpl { |
|
45 |
|
46 /* |
|
47 * A subset of the line number info that is softly cached |
|
48 */ |
|
49 static private class SoftLocationXRefs { |
|
50 final String stratumID; // The stratum of this information |
|
51 final Map<Integer, List<Location>> lineMapper; // Maps line number to location(s) |
|
52 final List<Location> lineLocations; // List of locations ordered by code index |
|
53 |
|
54 /* |
|
55 * Note: these do not necessarily correspond to |
|
56 * the line numbers of the first and last elements |
|
57 * in the lineLocations list. Use these only for bounds |
|
58 * checking and with lineMapper. |
|
59 */ |
|
60 final int lowestLine; |
|
61 final int highestLine; |
|
62 |
|
63 SoftLocationXRefs(String stratumID, Map<Integer, List<Location>> lineMapper, List<Location> lineLocations, |
|
64 int lowestLine, int highestLine) { |
|
65 this.stratumID = stratumID; |
|
66 this.lineMapper = Collections.unmodifiableMap(lineMapper); |
|
67 this.lineLocations = |
|
68 Collections.unmodifiableList(lineLocations); |
|
69 this.lowestLine = lowestLine; |
|
70 this.highestLine = highestLine; |
|
71 } |
|
72 } |
|
73 |
|
74 private Location location = null; |
|
75 private SoftReference<SoftLocationXRefs> softBaseLocationXRefsRef; |
|
76 private SoftReference<SoftLocationXRefs> softOtherLocationXRefsRef; |
|
77 private SoftReference<List<LocalVariable>> variablesRef = null; |
|
78 private boolean absentVariableInformation = false; |
|
79 private long firstIndex = -1; |
|
80 private long lastIndex = -1; |
|
81 private SoftReference<byte[]> bytecodesRef = null; |
|
82 private int argSlotCount = -1; |
|
83 |
|
84 ConcreteMethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType, |
|
85 long ref, |
|
86 String name, String signature, |
|
87 String genericSignature, int modifiers) { |
|
88 |
|
89 // The generic signature is set when this is created |
|
90 super(vm, declaringType, ref, name, signature, |
|
91 genericSignature, modifiers); |
|
92 } |
|
93 |
|
94 public Location location() { |
|
95 if (location == null) { |
|
96 getBaseLocations(); |
|
97 } |
|
98 return location; |
|
99 } |
|
100 |
|
101 List<Location> sourceNameFilter(List<Location> list, |
|
102 SDE.Stratum stratum, |
|
103 String sourceName) |
|
104 throws AbsentInformationException { |
|
105 if (sourceName == null) { |
|
106 return list; |
|
107 } else { |
|
108 /* needs sourceName filteration */ |
|
109 List<Location> locs = new ArrayList<Location>(); |
|
110 for (Location loc : list) { |
|
111 if (((LocationImpl)loc).sourceName(stratum).equals(sourceName)) { |
|
112 locs.add(loc); |
|
113 } |
|
114 } |
|
115 return locs; |
|
116 } |
|
117 } |
|
118 |
|
119 List<Location> allLineLocations(SDE.Stratum stratum, |
|
120 String sourceName) |
|
121 throws AbsentInformationException { |
|
122 List<Location> lineLocations = getLocations(stratum).lineLocations; |
|
123 |
|
124 if (lineLocations.size() == 0) { |
|
125 throw new AbsentInformationException(); |
|
126 } |
|
127 |
|
128 return Collections.unmodifiableList( |
|
129 sourceNameFilter(lineLocations, stratum, sourceName)); |
|
130 } |
|
131 |
|
132 List<Location> locationsOfLine(SDE.Stratum stratum, |
|
133 String sourceName, |
|
134 int lineNumber) |
|
135 throws AbsentInformationException { |
|
136 SoftLocationXRefs info = getLocations(stratum); |
|
137 |
|
138 if (info.lineLocations.size() == 0) { |
|
139 throw new AbsentInformationException(); |
|
140 } |
|
141 |
|
142 /* |
|
143 * Find the locations which match the line number |
|
144 * passed in. |
|
145 */ |
|
146 List<Location> list = info.lineMapper.get(new Integer(lineNumber)); |
|
147 |
|
148 if (list == null) { |
|
149 list = new ArrayList<Location>(0); |
|
150 } |
|
151 return Collections.unmodifiableList( |
|
152 sourceNameFilter(list, stratum, sourceName)); |
|
153 } |
|
154 |
|
155 |
|
156 public Location locationOfCodeIndex(long codeIndex) { |
|
157 if (firstIndex == -1) { |
|
158 getBaseLocations(); |
|
159 } |
|
160 |
|
161 /* |
|
162 * Check for invalid code index. |
|
163 */ |
|
164 if (codeIndex < firstIndex || codeIndex > lastIndex) { |
|
165 return null; |
|
166 } |
|
167 |
|
168 return new LocationImpl(virtualMachine(), this, codeIndex); |
|
169 } |
|
170 |
|
171 |
|
172 LineInfo codeIndexToLineInfo(SDE.Stratum stratum, |
|
173 long codeIndex) { |
|
174 if (firstIndex == -1) { |
|
175 getBaseLocations(); |
|
176 } |
|
177 |
|
178 /* |
|
179 * Check for invalid code index. |
|
180 */ |
|
181 if (codeIndex < firstIndex || codeIndex > lastIndex) { |
|
182 throw new InternalError( |
|
183 "Location with invalid code index"); |
|
184 } |
|
185 |
|
186 List<Location> lineLocations = getLocations(stratum).lineLocations; |
|
187 |
|
188 /* |
|
189 * Check for absent line numbers. |
|
190 */ |
|
191 if (lineLocations.size() == 0) { |
|
192 return super.codeIndexToLineInfo(stratum, codeIndex); |
|
193 } |
|
194 |
|
195 Iterator iter = lineLocations.iterator(); |
|
196 /* |
|
197 * Treat code before the beginning of the first line table |
|
198 * entry as part of the first line. javac will generate |
|
199 * code like this for some local classes. This "prolog" |
|
200 * code contains assignments from locals in the enclosing |
|
201 * scope to synthetic fields in the local class. Same for |
|
202 * other language prolog code. |
|
203 */ |
|
204 LocationImpl bestMatch = (LocationImpl)iter.next(); |
|
205 while (iter.hasNext()) { |
|
206 LocationImpl current = (LocationImpl)iter.next(); |
|
207 if (current.codeIndex() > codeIndex) { |
|
208 break; |
|
209 } |
|
210 bestMatch = current; |
|
211 } |
|
212 return bestMatch.getLineInfo(stratum); |
|
213 } |
|
214 |
|
215 |
|
216 public List<LocalVariable> variables() throws AbsentInformationException { |
|
217 return getVariables(); |
|
218 } |
|
219 |
|
220 public List<LocalVariable> variablesByName(String name) throws AbsentInformationException { |
|
221 List<LocalVariable> variables = getVariables(); |
|
222 |
|
223 List<LocalVariable> retList = new ArrayList<LocalVariable>(2); |
|
224 Iterator iter = variables.iterator(); |
|
225 while(iter.hasNext()) { |
|
226 LocalVariable variable = (LocalVariable)iter.next(); |
|
227 if (variable.name().equals(name)) { |
|
228 retList.add(variable); |
|
229 } |
|
230 } |
|
231 return retList; |
|
232 } |
|
233 |
|
234 public List<LocalVariable> arguments() throws AbsentInformationException { |
|
235 List<LocalVariable> variables = getVariables(); |
|
236 |
|
237 List<LocalVariable> retList = new ArrayList<LocalVariable>(variables.size()); |
|
238 Iterator iter = variables.iterator(); |
|
239 while(iter.hasNext()) { |
|
240 LocalVariable variable = (LocalVariable)iter.next(); |
|
241 if (variable.isArgument()) { |
|
242 retList.add(variable); |
|
243 } |
|
244 } |
|
245 return retList; |
|
246 } |
|
247 |
|
248 public byte[] bytecodes() { |
|
249 byte[] bytecodes = (bytecodesRef == null) ? null : |
|
250 (byte[])bytecodesRef.get(); |
|
251 if (bytecodes == null) { |
|
252 try { |
|
253 bytecodes = JDWP.Method.Bytecodes. |
|
254 process(vm, declaringType, ref).bytes; |
|
255 } catch (JDWPException exc) { |
|
256 throw exc.toJDIException(); |
|
257 } |
|
258 bytecodesRef = new SoftReference<byte[]>(bytecodes); |
|
259 } |
|
260 /* |
|
261 * Arrays are always modifiable, so it is a little unsafe |
|
262 * to return the cached bytecodes directly; instead, we |
|
263 * make a clone at the cost of using more memory. |
|
264 */ |
|
265 return (byte[])bytecodes.clone(); |
|
266 } |
|
267 |
|
268 int argSlotCount() throws AbsentInformationException { |
|
269 if (argSlotCount == -1) { |
|
270 getVariables(); |
|
271 } |
|
272 return argSlotCount; |
|
273 } |
|
274 |
|
275 private SoftLocationXRefs getLocations(SDE.Stratum stratum) { |
|
276 if (stratum.isJava()) { |
|
277 return getBaseLocations(); |
|
278 } |
|
279 String stratumID = stratum.id(); |
|
280 SoftLocationXRefs info = |
|
281 (softOtherLocationXRefsRef == null) ? null : |
|
282 (SoftLocationXRefs)softOtherLocationXRefsRef.get(); |
|
283 if (info != null && info.stratumID.equals(stratumID)) { |
|
284 return info; |
|
285 } |
|
286 |
|
287 List<Location> lineLocations = new ArrayList<Location>(); |
|
288 Map<Integer, List<Location>> lineMapper = new HashMap<Integer, List<Location>>(); |
|
289 int lowestLine = -1; |
|
290 int highestLine = -1; |
|
291 SDE.LineStratum lastLineStratum = null; |
|
292 SDE.Stratum baseStratum = |
|
293 declaringType.stratum(SDE.BASE_STRATUM_NAME); |
|
294 Iterator it = getBaseLocations().lineLocations.iterator(); |
|
295 while(it.hasNext()) { |
|
296 LocationImpl loc = (LocationImpl)it.next(); |
|
297 int baseLineNumber = loc.lineNumber(baseStratum); |
|
298 SDE.LineStratum lineStratum = |
|
299 stratum.lineStratum(declaringType, |
|
300 baseLineNumber); |
|
301 |
|
302 if (lineStratum == null) { |
|
303 // location not mapped in this stratum |
|
304 continue; |
|
305 } |
|
306 |
|
307 int lineNumber = lineStratum.lineNumber(); |
|
308 |
|
309 // remove unmapped and dup lines |
|
310 if ((lineNumber != -1) && |
|
311 (!lineStratum.equals(lastLineStratum))) { |
|
312 lastLineStratum = lineStratum; |
|
313 |
|
314 // Remember the largest/smallest line number |
|
315 if (lineNumber > highestLine) { |
|
316 highestLine = lineNumber; |
|
317 } |
|
318 if ((lineNumber < lowestLine) || (lowestLine == -1)) { |
|
319 lowestLine = lineNumber; |
|
320 } |
|
321 |
|
322 loc.addStratumLineInfo( |
|
323 new StratumLineInfo(stratumID, |
|
324 lineNumber, |
|
325 lineStratum.sourceName(), |
|
326 lineStratum.sourcePath())); |
|
327 |
|
328 // Add to the location list |
|
329 lineLocations.add(loc); |
|
330 |
|
331 // Add to the line -> locations map |
|
332 Integer key = new Integer(lineNumber); |
|
333 List<Location> mappedLocs = lineMapper.get(key); |
|
334 if (mappedLocs == null) { |
|
335 mappedLocs = new ArrayList<Location>(1); |
|
336 lineMapper.put(key, mappedLocs); |
|
337 } |
|
338 mappedLocs.add(loc); |
|
339 } |
|
340 } |
|
341 |
|
342 info = new SoftLocationXRefs(stratumID, |
|
343 lineMapper, lineLocations, |
|
344 lowestLine, highestLine); |
|
345 softOtherLocationXRefsRef = new SoftReference<SoftLocationXRefs>(info); |
|
346 return info; |
|
347 } |
|
348 |
|
349 private SoftLocationXRefs getBaseLocations() { |
|
350 SoftLocationXRefs info = (softBaseLocationXRefsRef == null) ? null : |
|
351 (SoftLocationXRefs)softBaseLocationXRefsRef.get(); |
|
352 if (info != null) { |
|
353 return info; |
|
354 } |
|
355 |
|
356 JDWP.Method.LineTable lntab = null; |
|
357 try { |
|
358 lntab = JDWP.Method.LineTable.process(vm, declaringType, ref); |
|
359 } catch (JDWPException exc) { |
|
360 /* |
|
361 * Note: the absent info error shouldn't happen here |
|
362 * because the first and last index are always available. |
|
363 */ |
|
364 throw exc.toJDIException(); |
|
365 } |
|
366 |
|
367 int count = lntab.lines.length; |
|
368 |
|
369 List<Location> lineLocations = new ArrayList<Location>(count); |
|
370 Map<Integer, List<Location>>lineMapper = new HashMap<Integer, List<Location>>(); |
|
371 int lowestLine = -1; |
|
372 int highestLine = -1; |
|
373 for (int i = 0; i < count; i++) { |
|
374 long bci = lntab.lines[i].lineCodeIndex; |
|
375 int lineNumber = lntab.lines[i].lineNumber; |
|
376 |
|
377 /* |
|
378 * Some compilers will point multiple consecutive |
|
379 * lines at the same location. We need to choose |
|
380 * one of them so that we can consistently map back |
|
381 * and forth between line and location. So we choose |
|
382 * to record only the last line entry at a particular |
|
383 * location. |
|
384 */ |
|
385 if ((i + 1 == count) || (bci != lntab.lines[i+1].lineCodeIndex)) { |
|
386 // Remember the largest/smallest line number |
|
387 if (lineNumber > highestLine) { |
|
388 highestLine = lineNumber; |
|
389 } |
|
390 if ((lineNumber < lowestLine) || (lowestLine == -1)) { |
|
391 lowestLine = lineNumber; |
|
392 } |
|
393 LocationImpl loc = |
|
394 new LocationImpl(virtualMachine(), this, bci); |
|
395 loc.addBaseLineInfo( |
|
396 new BaseLineInfo(lineNumber, declaringType)); |
|
397 |
|
398 // Add to the location list |
|
399 lineLocations.add(loc); |
|
400 |
|
401 // Add to the line -> locations map |
|
402 Integer key = new Integer(lineNumber); |
|
403 List<Location> mappedLocs = lineMapper.get(key); |
|
404 if (mappedLocs == null) { |
|
405 mappedLocs = new ArrayList<Location>(1); |
|
406 lineMapper.put(key, mappedLocs); |
|
407 } |
|
408 mappedLocs.add(loc); |
|
409 } |
|
410 } |
|
411 |
|
412 /* |
|
413 * firstIndex, lastIndex, and startLocation need to be |
|
414 * retrieved only once since they are strongly referenced. |
|
415 */ |
|
416 if (location == null) { |
|
417 firstIndex = lntab.start; |
|
418 lastIndex = lntab.end; |
|
419 /* |
|
420 * The startLocation is the first one in the |
|
421 * location list if we have one; |
|
422 * otherwise, we construct a location for a |
|
423 * method start with no line info |
|
424 */ |
|
425 if (count > 0) { |
|
426 location = lineLocations.get(0); |
|
427 } else { |
|
428 location = new LocationImpl(virtualMachine(), this, |
|
429 firstIndex); |
|
430 } |
|
431 } |
|
432 |
|
433 info = new SoftLocationXRefs(SDE.BASE_STRATUM_NAME, |
|
434 lineMapper, lineLocations, |
|
435 lowestLine, highestLine); |
|
436 softBaseLocationXRefsRef = new SoftReference<SoftLocationXRefs>(info); |
|
437 return info; |
|
438 } |
|
439 |
|
440 private List<LocalVariable> getVariables1_4() throws AbsentInformationException { |
|
441 JDWP.Method.VariableTable vartab = null; |
|
442 try { |
|
443 vartab = JDWP.Method.VariableTable. |
|
444 process(vm, declaringType, ref); |
|
445 } catch (JDWPException exc) { |
|
446 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) { |
|
447 absentVariableInformation = true; |
|
448 throw new AbsentInformationException(); |
|
449 } else { |
|
450 throw exc.toJDIException(); |
|
451 } |
|
452 } |
|
453 |
|
454 // Get the number of slots used by argument variables |
|
455 argSlotCount = vartab.argCnt; |
|
456 int count = vartab.slots.length; |
|
457 List<LocalVariable> variables = new ArrayList<LocalVariable>(count); |
|
458 for (int i=0; i<count; i++) { |
|
459 JDWP.Method.VariableTable.SlotInfo si = vartab.slots[i]; |
|
460 |
|
461 /* |
|
462 * Skip "this*" entries because they are never real |
|
463 * variables from the JLS perspective. |
|
464 */ |
|
465 if (!si.name.startsWith("this$") && !si.name.equals("this")) { |
|
466 Location scopeStart = new LocationImpl(virtualMachine(), |
|
467 this, si.codeIndex); |
|
468 Location scopeEnd = |
|
469 new LocationImpl(virtualMachine(), this, |
|
470 si.codeIndex + si.length - 1); |
|
471 LocalVariable variable = |
|
472 new LocalVariableImpl(virtualMachine(), this, |
|
473 si.slot, scopeStart, scopeEnd, |
|
474 si.name, si.signature, null); |
|
475 // Add to the variable list |
|
476 variables.add(variable); |
|
477 } |
|
478 } |
|
479 return variables; |
|
480 } |
|
481 |
|
482 private List<LocalVariable> getVariables1() throws AbsentInformationException { |
|
483 |
|
484 if (!vm.canGet1_5LanguageFeatures()) { |
|
485 return getVariables1_4(); |
|
486 } |
|
487 |
|
488 JDWP.Method.VariableTableWithGeneric vartab = null; |
|
489 try { |
|
490 vartab = JDWP.Method.VariableTableWithGeneric. |
|
491 process(vm, declaringType, ref); |
|
492 } catch (JDWPException exc) { |
|
493 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) { |
|
494 absentVariableInformation = true; |
|
495 throw new AbsentInformationException(); |
|
496 } else { |
|
497 throw exc.toJDIException(); |
|
498 } |
|
499 } |
|
500 |
|
501 // Get the number of slots used by argument variables |
|
502 argSlotCount = vartab.argCnt; |
|
503 int count = vartab.slots.length; |
|
504 List<LocalVariable> variables = new ArrayList<LocalVariable>(count); |
|
505 for (int i=0; i<count; i++) { |
|
506 JDWP.Method.VariableTableWithGeneric.SlotInfo si = vartab.slots[i]; |
|
507 |
|
508 /* |
|
509 * Skip "this*" entries because they are never real |
|
510 * variables from the JLS perspective. |
|
511 */ |
|
512 if (!si.name.startsWith("this$") && !si.name.equals("this")) { |
|
513 Location scopeStart = new LocationImpl(virtualMachine(), |
|
514 this, si.codeIndex); |
|
515 Location scopeEnd = |
|
516 new LocationImpl(virtualMachine(), this, |
|
517 si.codeIndex + si.length - 1); |
|
518 LocalVariable variable = |
|
519 new LocalVariableImpl(virtualMachine(), this, |
|
520 si.slot, scopeStart, scopeEnd, |
|
521 si.name, si.signature, |
|
522 si.genericSignature); |
|
523 // Add to the variable list |
|
524 variables.add(variable); |
|
525 } |
|
526 } |
|
527 return variables; |
|
528 } |
|
529 |
|
530 private List<LocalVariable> getVariables() throws AbsentInformationException { |
|
531 if (absentVariableInformation) { |
|
532 throw new AbsentInformationException(); |
|
533 } |
|
534 |
|
535 List<LocalVariable> variables = (variablesRef == null) ? null : |
|
536 variablesRef.get(); |
|
537 if (variables != null) { |
|
538 return variables; |
|
539 } |
|
540 variables = getVariables1(); |
|
541 variables = Collections.unmodifiableList(variables); |
|
542 variablesRef = new SoftReference<List<LocalVariable>>(variables); |
|
543 return variables; |
|
544 } |
|
545 } |