• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * 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 
SoftLocationXRefs(String stratumID, Map<Integer, List<Location>> lineMapper, List<Location> lineLocations, int lowestLine, int highestLine)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 
ConcreteMethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType, long ref, String name, String signature, String genericSignature, int modifiers)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 
location()94     public Location location() {
95         if (location == null) {
96             getBaseLocations();
97         }
98         return location;
99     }
100 
sourceNameFilter(List<Location> list, SDE.Stratum stratum, String sourceName)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 
allLineLocations(SDE.Stratum stratum, String sourceName)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 
locationsOfLine(SDE.Stratum stratum, String sourceName, int lineNumber)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 
locationOfCodeIndex(long codeIndex)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 
codeIndexToLineInfo(SDE.Stratum stratum, long codeIndex)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<Location> 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 
variables()216     public List<LocalVariable> variables() throws AbsentInformationException {
217         return getVariables();
218     }
219 
variablesByName(String name)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<LocalVariable> iter = variables.iterator();
225         while(iter.hasNext()) {
226             LocalVariable variable = iter.next();
227             if (variable.name().equals(name)) {
228                 retList.add(variable);
229             }
230         }
231         return retList;
232     }
233 
arguments()234     public List<LocalVariable> arguments() throws AbsentInformationException {
235         List<LocalVariable> variables = getVariables();
236 
237         List<LocalVariable> retList = new ArrayList<LocalVariable>(variables.size());
238         Iterator<LocalVariable> iter = variables.iterator();
239         while(iter.hasNext()) {
240             LocalVariable variable = iter.next();
241             if (variable.isArgument()) {
242                 retList.add(variable);
243             }
244         }
245         return retList;
246     }
247 
bytecodes()248     public byte[] bytecodes() {
249         byte[] bytecodes = (bytecodesRef == null) ? null :
250                                      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 bytecodes.clone();
266     }
267 
argSlotCount()268     int argSlotCount() throws AbsentInformationException {
269         if (argSlotCount == -1) {
270             getVariables();
271         }
272         return argSlotCount;
273     }
274 
getLocations(SDE.Stratum stratum)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                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<Location> 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 
getBaseLocations()349     private SoftLocationXRefs getBaseLocations() {
350         SoftLocationXRefs info = (softBaseLocationXRefsRef == null) ? null :
351                                      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 
getVariables1_4()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 
getVariables1()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 
getVariables()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 }
546