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 26package com.sun.tools.jdi; 27 28import java.lang.ref.SoftReference; 29import java.util.ArrayList; 30import java.util.Collections; 31import java.util.HashMap; 32import java.util.Iterator; 33import java.util.List; 34import java.util.Map; 35 36import com.sun.jdi.AbsentInformationException; 37import com.sun.jdi.LocalVariable; 38import com.sun.jdi.Location; 39import com.sun.jdi.VirtualMachine; 40import com.sun.tools.jdi.JDWP.Method.VariableTable; 41import 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 */ 48public 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 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 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 97 public Location location() { 98 if (location == null) { 99 getBaseLocations(); 100 } 101 return location; 102 } 103 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 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 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 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 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 215 public List<LocalVariable> variables() throws AbsentInformationException { 216 return getVariables(); 217 } 218 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 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 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 267 int argSlotCount() throws AbsentInformationException { 268 if (argSlotCount == -1) { 269 getVariables(); 270 } 271 return argSlotCount; 272 } 273 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 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 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 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 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