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