Path: blob/master/src/jdk.jdi/share/classes/com/sun/tools/jdi/ConcreteMethodImpl.java
41161 views
/*1* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.jdi;2627import java.lang.ref.SoftReference;28import java.util.ArrayList;29import java.util.Collections;30import java.util.HashMap;31import java.util.Iterator;32import java.util.List;33import java.util.Map;3435import com.sun.jdi.AbsentInformationException;36import com.sun.jdi.LocalVariable;37import com.sun.jdi.Location;38import com.sun.jdi.VirtualMachine;39import com.sun.tools.jdi.JDWP.Method.VariableTable;40import com.sun.tools.jdi.JDWP.Method.VariableTableWithGeneric;4142/**43* Represents methods with method bodies.44* That is, non-native non-abstract methods.45* Private to MethodImpl.46*/47public class ConcreteMethodImpl extends MethodImpl {4849/*50* A subset of the line number info that is softly cached51*/52static private class SoftLocationXRefs {53final String stratumID; // The stratum of this information54final Map<Integer, List<Location>> lineMapper; // Maps line number to location(s)55final List<Location> lineLocations; // List of locations ordered by code index5657/*58* Note: these do not necessarily correspond to59* the line numbers of the first and last elements60* in the lineLocations list. Use these only for bounds61* checking and with lineMapper.62*/63@SuppressWarnings("unused")64final int lowestLine;65@SuppressWarnings("unused")66final int highestLine;6768SoftLocationXRefs(String stratumID, Map<Integer, List<Location>> lineMapper,69List<Location> lineLocations, int lowestLine, int highestLine) {70this.stratumID = stratumID;71this.lineMapper = Collections.unmodifiableMap(lineMapper);72this.lineLocations = Collections.unmodifiableList(lineLocations);73this.lowestLine = lowestLine;74this.highestLine = highestLine;75}76}7778private Location location = null;79private SoftReference<SoftLocationXRefs> softBaseLocationXRefsRef;80private SoftReference<SoftLocationXRefs> softOtherLocationXRefsRef;81private SoftReference<List<LocalVariable>> variablesRef = null;82private boolean absentVariableInformation = false;83private long firstIndex = -1;84private long lastIndex = -1;85private SoftReference<byte[]> bytecodesRef = null;86private int argSlotCount = -1;8788ConcreteMethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType,89long ref, String name, String signature,90String genericSignature, int modifiers)91{92// The generic signature is set when this is created93super(vm, declaringType, ref, name, signature, genericSignature, modifiers);94}9596public Location location() {97if (location == null) {98getBaseLocations();99}100return location;101}102103List<Location> sourceNameFilter(List<Location> list,104SDE.Stratum stratum,105String sourceName)106throws AbsentInformationException {107if (sourceName == null) {108return list;109} else {110/* needs sourceName filteration */111List<Location> locs = new ArrayList<>();112for (Location loc : list) {113if (((LocationImpl)loc).sourceName(stratum).equals(sourceName)) {114locs.add(loc);115}116}117return locs;118}119}120121List<Location> allLineLocations(SDE.Stratum stratum,122String sourceName)123throws AbsentInformationException {124List<Location> lineLocations = getLocations(stratum).lineLocations;125126if (lineLocations.size() == 0) {127throw new AbsentInformationException();128}129130return Collections.unmodifiableList(131sourceNameFilter(lineLocations, stratum, sourceName));132}133134List<Location> locationsOfLine(SDE.Stratum stratum,135String sourceName,136int lineNumber)137throws AbsentInformationException {138SoftLocationXRefs info = getLocations(stratum);139140if (info.lineLocations.size() == 0) {141throw new AbsentInformationException();142}143144/*145* Find the locations which match the line number146* passed in.147*/148List<Location> list = info.lineMapper.get(lineNumber);149150if (list == null) {151list = new ArrayList<>(0);152}153return Collections.unmodifiableList(154sourceNameFilter(list, stratum, sourceName));155}156157public Location locationOfCodeIndex(long codeIndex) {158if (firstIndex == -1) {159getBaseLocations();160}161162/*163* Check for invalid code index.164*/165if (codeIndex < firstIndex || codeIndex > lastIndex) {166return null;167}168169return new LocationImpl(virtualMachine(), this, codeIndex);170}171172LineInfo codeIndexToLineInfo(SDE.Stratum stratum,173long codeIndex) {174if (firstIndex == -1) {175getBaseLocations();176}177178/*179* Check for invalid code index.180*/181if (codeIndex < firstIndex || codeIndex > lastIndex) {182throw new InternalError("Location with invalid code index");183}184185List<Location> lineLocations = getLocations(stratum).lineLocations;186187/*188* Check for absent line numbers.189*/190if (lineLocations.size() == 0) {191return super.codeIndexToLineInfo(stratum, codeIndex);192}193194Iterator<Location> iter = lineLocations.iterator();195/*196* Treat code before the beginning of the first line table197* entry as part of the first line. javac will generate198* code like this for some local classes. This "prolog"199* code contains assignments from locals in the enclosing200* scope to synthetic fields in the local class. Same for201* other language prolog code.202*/203LocationImpl bestMatch = (LocationImpl)iter.next();204while (iter.hasNext()) {205LocationImpl current = (LocationImpl)iter.next();206if (current.codeIndex() > codeIndex) {207break;208}209bestMatch = current;210}211return bestMatch.getLineInfo(stratum);212}213214public List<LocalVariable> variables() throws AbsentInformationException {215return getVariables();216}217218public List<LocalVariable> variablesByName(String name) throws AbsentInformationException {219List<LocalVariable> variables = getVariables();220221List<LocalVariable> retList = new ArrayList<>(2);222Iterator<LocalVariable> iter = variables.iterator();223while(iter.hasNext()) {224LocalVariable variable = iter.next();225if (variable.name().equals(name)) {226retList.add(variable);227}228}229return retList;230}231232public List<LocalVariable> arguments() throws AbsentInformationException {233List<LocalVariable> variables = getVariables();234235List<LocalVariable> retList = new ArrayList<>(variables.size());236Iterator<LocalVariable> iter = variables.iterator();237while(iter.hasNext()) {238LocalVariable variable = iter.next();239if (variable.isArgument()) {240retList.add(variable);241}242}243return retList;244}245246public byte[] bytecodes() {247byte[] bytecodes = (bytecodesRef == null) ? null :248bytecodesRef.get();249if (bytecodes == null) {250try {251bytecodes = JDWP.Method.Bytecodes.252process(vm, declaringType, ref).bytes;253} catch (JDWPException exc) {254throw exc.toJDIException();255}256bytecodesRef = new SoftReference<>(bytecodes);257}258/*259* Arrays are always modifiable, so it is a little unsafe260* to return the cached bytecodes directly; instead, we261* make a clone at the cost of using more memory.262*/263return bytecodes.clone();264}265266int argSlotCount() throws AbsentInformationException {267if (argSlotCount == -1) {268getVariables();269}270return argSlotCount;271}272273private SoftLocationXRefs getLocations(SDE.Stratum stratum) {274if (stratum.isJava()) {275return getBaseLocations();276}277String stratumID = stratum.id();278SoftLocationXRefs info =279(softOtherLocationXRefsRef == null) ? null :280softOtherLocationXRefsRef.get();281if (info != null && info.stratumID.equals(stratumID)) {282return info;283}284285List<Location> lineLocations = new ArrayList<Location>();286Map<Integer, List<Location>> lineMapper = new HashMap<>();287int lowestLine = -1;288int highestLine = -1;289SDE.LineStratum lastLineStratum = null;290SDE.Stratum baseStratum = declaringType.stratum(SDE.BASE_STRATUM_NAME);291Iterator<Location> it = getBaseLocations().lineLocations.iterator();292while(it.hasNext()) {293LocationImpl loc = (LocationImpl)it.next();294int baseLineNumber = loc.lineNumber(baseStratum);295SDE.LineStratum lineStratum =296stratum.lineStratum(declaringType, baseLineNumber);297298if (lineStratum == null) {299// location not mapped in this stratum300continue;301}302303int lineNumber = lineStratum.lineNumber();304305// remove unmapped and dup lines306if ((lineNumber != -1) &&307(!lineStratum.equals(lastLineStratum))) {308lastLineStratum = lineStratum;309310// Remember the largest/smallest line number311if (lineNumber > highestLine) {312highestLine = lineNumber;313}314if ((lineNumber < lowestLine) || (lowestLine == -1)) {315lowestLine = lineNumber;316}317318loc.addStratumLineInfo(319new StratumLineInfo(stratumID,320lineNumber,321lineStratum.sourceName(),322lineStratum.sourcePath()));323324// Add to the location list325lineLocations.add(loc);326327// Add to the line -> locations map328Integer key = lineNumber;329List<Location> mappedLocs = lineMapper.get(key);330if (mappedLocs == null) {331mappedLocs = new ArrayList<Location>(1);332lineMapper.put(key, mappedLocs);333}334mappedLocs.add(loc);335}336}337338info = new SoftLocationXRefs(stratumID, lineMapper, lineLocations,339lowestLine, highestLine);340softOtherLocationXRefsRef = new SoftReference<>(info);341return info;342}343344private SoftLocationXRefs getBaseLocations() {345SoftLocationXRefs info = (softBaseLocationXRefsRef == null) ? null :346softBaseLocationXRefsRef.get();347if (info != null) {348return info;349}350351JDWP.Method.LineTable lntab = null;352try {353lntab = JDWP.Method.LineTable.process(vm, declaringType, ref);354} catch (JDWPException exc) {355/*356* Note: the absent info error shouldn't happen here357* because the first and last index are always available.358*/359throw exc.toJDIException();360}361362int count = lntab.lines.length;363364List<Location> lineLocations = new ArrayList<>(count);365Map<Integer, List<Location>>lineMapper = new HashMap<>();366int lowestLine = -1;367int highestLine = -1;368for (int i = 0; i < count; i++) {369long bci = lntab.lines[i].lineCodeIndex;370int lineNumber = lntab.lines[i].lineNumber;371372/*373* Some compilers will point multiple consecutive374* lines at the same location. We need to choose375* one of them so that we can consistently map back376* and forth between line and location. So we choose377* to record only the last line entry at a particular378* location.379*/380if ((i + 1 == count) || (bci != lntab.lines[i+1].lineCodeIndex)) {381// Remember the largest/smallest line number382if (lineNumber > highestLine) {383highestLine = lineNumber;384}385if ((lineNumber < lowestLine) || (lowestLine == -1)) {386lowestLine = lineNumber;387}388LocationImpl loc =389new LocationImpl(virtualMachine(), this, bci);390loc.addBaseLineInfo(391new BaseLineInfo(lineNumber, declaringType));392393// Add to the location list394lineLocations.add(loc);395396// Add to the line -> locations map397Integer key = lineNumber;398List<Location> mappedLocs = lineMapper.get(key);399if (mappedLocs == null) {400mappedLocs = new ArrayList<>(1);401lineMapper.put(key, mappedLocs);402}403mappedLocs.add(loc);404}405}406407/*408* firstIndex, lastIndex, and startLocation need to be409* retrieved only once since they are strongly referenced.410*/411if (location == null) {412firstIndex = lntab.start;413lastIndex = lntab.end;414/*415* The startLocation is the first one in the416* location list if we have one;417* otherwise, we construct a location for a418* method start with no line info419*/420if (count > 0) {421location = lineLocations.get(0);422} else {423location = new LocationImpl(virtualMachine(), this,424firstIndex);425}426}427428info = new SoftLocationXRefs(SDE.BASE_STRATUM_NAME,429lineMapper, lineLocations,430lowestLine, highestLine);431softBaseLocationXRefsRef = new SoftReference<SoftLocationXRefs>(info);432return info;433}434435private List<LocalVariable> getVariables1_4() throws AbsentInformationException {436JDWP.Method.VariableTable vartab = null;437try {438vartab = JDWP.Method.VariableTable.439process(vm, declaringType, ref);440} catch (JDWPException exc) {441if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {442absentVariableInformation = true;443throw new AbsentInformationException();444} else {445throw exc.toJDIException();446}447}448449// Get the number of slots used by argument variables450argSlotCount = vartab.argCnt;451int count = vartab.slots.length;452List<LocalVariable> variables = new ArrayList<>(count);453for (int i=0; i<count; i++) {454JDWP.Method.VariableTable.SlotInfo si = vartab.slots[i];455456/*457* Skip "this*" entries because they are never real458* variables from the JLS perspective.459*/460if (!si.name.startsWith("this$") && !si.name.equals("this")) {461Location scopeStart = new LocationImpl(virtualMachine(),462this, si.codeIndex);463Location scopeEnd =464new LocationImpl(virtualMachine(), this,465si.codeIndex + si.length - 1);466LocalVariable variable =467new LocalVariableImpl(virtualMachine(), this,468si.slot, scopeStart, scopeEnd,469si.name, si.signature, null);470// Add to the variable list471variables.add(variable);472}473}474return variables;475}476477private List<LocalVariable> getVariables1() throws AbsentInformationException {478479if (!vm.canGet1_5LanguageFeatures()) {480return getVariables1_4();481}482483JDWP.Method.VariableTableWithGeneric vartab = null;484try {485vartab = JDWP.Method.VariableTableWithGeneric.486process(vm, declaringType, ref);487} catch (JDWPException exc) {488if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {489absentVariableInformation = true;490throw new AbsentInformationException();491} else {492throw exc.toJDIException();493}494}495496// Get the number of slots used by argument variables497argSlotCount = vartab.argCnt;498int count = vartab.slots.length;499List<LocalVariable> variables = new ArrayList<LocalVariable>(count);500for (int i=0; i<count; i++) {501JDWP.Method.VariableTableWithGeneric.SlotInfo si = vartab.slots[i];502503/*504* Skip "this*" entries because they are never real505* variables from the JLS perspective.506*/507if (!si.name.startsWith("this$") && !si.name.equals("this")) {508Location scopeStart = new LocationImpl(virtualMachine(),509this, si.codeIndex);510Location scopeEnd =511new LocationImpl(virtualMachine(), this,512si.codeIndex + si.length - 1);513LocalVariable variable =514new LocalVariableImpl(virtualMachine(), this,515si.slot, scopeStart, scopeEnd,516si.name, si.signature,517si.genericSignature);518// Add to the variable list519variables.add(variable);520}521}522return variables;523}524525private List<LocalVariable> getVariables() throws AbsentInformationException {526if (absentVariableInformation) {527throw new AbsentInformationException();528}529530List<LocalVariable> variables = (variablesRef == null) ? null :531variablesRef.get();532if (variables != null) {533return variables;534}535variables = getVariables1();536variables = Collections.unmodifiableList(variables);537variablesRef = new SoftReference<>(variables);538return variables;539}540}541542543