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