| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Yavor Boyadzhiev <yavor.vasilev.boyadzhiev@sap.com> - Bug 162399 |
| * Jesper Steen Møller <jesper@selskabet.org> - Bug 430839 |
| *******************************************************************************/ |
| package org.eclipse.jdi.internal; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket; |
| import org.eclipse.jdi.internal.jdwp.JdwpFieldID; |
| import org.eclipse.jdi.internal.jdwp.JdwpID; |
| import org.eclipse.jdi.internal.jdwp.JdwpMethodID; |
| import org.eclipse.jdi.internal.jdwp.JdwpReferenceTypeID; |
| import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket; |
| import org.eclipse.osgi.util.NLS; |
| |
| import com.sun.jdi.AbsentInformationException; |
| import com.sun.jdi.ClassLoaderReference; |
| import com.sun.jdi.ClassNotLoadedException; |
| import com.sun.jdi.ClassNotPreparedException; |
| import com.sun.jdi.ClassObjectReference; |
| import com.sun.jdi.ClassType; |
| import com.sun.jdi.Field; |
| import com.sun.jdi.IncompatibleThreadStateException; |
| import com.sun.jdi.InterfaceType; |
| import com.sun.jdi.InternalException; |
| import com.sun.jdi.InvalidTypeException; |
| import com.sun.jdi.InvocationException; |
| import com.sun.jdi.Location; |
| import com.sun.jdi.Method; |
| import com.sun.jdi.NativeMethodException; |
| import com.sun.jdi.ObjectCollectedException; |
| import com.sun.jdi.ObjectReference; |
| import com.sun.jdi.ReferenceType; |
| import com.sun.jdi.ThreadReference; |
| import com.sun.jdi.VMDisconnectedException; |
| import com.sun.jdi.Value; |
| |
| /** |
| * this class implements the corresponding interfaces declared by the JDI |
| * specification. See the com.sun.jdi package for more information. |
| * |
| */ |
| public abstract class ReferenceTypeImpl extends TypeImpl implements |
| ReferenceType, org.eclipse.jdi.hcr.ReferenceType { |
| |
| /** ClassStatus Constants. */ |
| public static final int JDWP_CLASS_STATUS_VERIFIED = 1; |
| public static final int JDWP_CLASS_STATUS_PREPARED = 2; |
| public static final int JDWP_CLASS_STATUS_INITIALIZED = 4; |
| public static final int JDWP_CLASS_STATUS_ERROR = 8; |
| |
| /** Mapping of command codes to strings. */ |
| private static String[] fgClassStatusStrings = null; |
| |
| /** |
| * Represent the data about one file info contained in one stratum in the |
| * SMAP. |
| */ |
| protected static class FileInfo { |
| |
| /** |
| * The id. |
| */ |
| protected int fFileId; |
| |
| /** |
| * The name of the source file. |
| */ |
| protected String fFileName; |
| |
| /** |
| * The path of the source file. |
| */ |
| protected String fAbsoluteFileName; |
| |
| /** |
| * Map line number in the input source file -> list of [start line in |
| * the output source file, range in the output source file]. (Integer -> |
| * List of int[2]). |
| */ |
| private HashMap<Integer, List<int[]>> fLineInfo; |
| |
| /** |
| * FileInfo constructor. |
| * |
| * @param fileId |
| * the id. |
| * @param fileName |
| * the name of the source file. |
| * @param absoluteFileName |
| * the path of the source file (can be <code>null</code>). |
| */ |
| public FileInfo(int fileId, String fileName, String absoluteFileName) { |
| fFileId = fileId; |
| fFileName = fileName; |
| fAbsoluteFileName = absoluteFileName; |
| fLineInfo = new HashMap<>(); |
| } |
| |
| /** |
| * Add information about the mapping of one line. Associate a line in |
| * the input source file to a snippet of code in the output source file. |
| * |
| * @param inputLine |
| * the line number in the input source file. |
| * @param outputStartLine |
| * the number of the first line of the corresponding snippet |
| * in the output source file. |
| * @param outputLineRange |
| * the size of the corresponding snippet in the output source |
| * file. |
| */ |
| public void addLineInfo(int inputLine, int outputStartLine, |
| int outputLineRange) { |
| Integer key = new Integer(inputLine); |
| List<int[]> outputLines = fLineInfo.get(key); |
| if (outputLines == null) { |
| outputLines = new ArrayList<>(); |
| fLineInfo.put(key, outputLines); |
| } |
| outputLines.add(new int[] { outputStartLine, outputLineRange }); |
| } |
| |
| /** |
| * Return a list of line information about the code in the output source |
| * file associated to the given line in the input source file. |
| * |
| * @param lineNumber |
| * the line number in the input source file. |
| * @return a List of int[2]. |
| */ |
| public List<Integer> getOutputLinesForLine(int lineNumber) { |
| List<Integer> list = new ArrayList<>(); |
| List<int[]> outputLines = fLineInfo.get(new Integer(lineNumber)); |
| if (outputLines != null) { |
| for (Iterator<int[]> iter = outputLines.iterator(); iter.hasNext();) { |
| int[] info = iter.next(); |
| int outputLineNumber = info[0]; |
| int length = info[1]; |
| if (length == 0) { |
| length = length + 1; |
| } |
| for (int i = 0; i < length; i++) { |
| list.add(new Integer(outputLineNumber++)); |
| } |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| @Override |
| public boolean equals(Object object) { |
| if (!(object instanceof FileInfo)) { |
| return false; |
| } |
| return fFileId == ((FileInfo) object).fFileId; |
| } |
| |
| } |
| |
| /** |
| * Represent the information contained in the SMAP about one stratum. |
| */ |
| protected static class Stratum { |
| |
| /** |
| * The id of this stratum. |
| */ |
| private String fId; |
| |
| /** |
| * The file info data associated to this stratum. |
| */ |
| private List<FileInfo> fFileInfos; |
| |
| /** |
| * Id of the primary file for this stratum. |
| */ |
| private int fPrimaryFileId; |
| |
| /** |
| * Map line number in the output source file -> list of line numbers in |
| * the input source file. (Integer -> List of Integer) |
| */ |
| private HashMap<Integer, List<int[]>> fOutputLineToInputLine; |
| |
| /** |
| * Stratum constructor. |
| * |
| * @param id |
| * The id of this stratum. |
| */ |
| public Stratum(String id) { |
| fId = id; |
| fFileInfos = new ArrayList<>(); |
| fOutputLineToInputLine = new HashMap<>(); |
| fPrimaryFileId = -1; |
| } |
| |
| /** |
| * Add a file info to this stratum. |
| * |
| * @param fileId |
| * the id. |
| * @param fileName |
| * the name of the source file. |
| */ |
| public void addFileInfo(int fileId, String fileName) |
| throws AbsentInformationException { |
| addFileInfo(fileId, fileName, null); |
| } |
| |
| /** |
| * Add a file info to this stratum. |
| * |
| * @param fileId |
| * the id. |
| * @param fileName |
| * the name of the source file. |
| * @param absoluteFileName |
| * the path of the source file. |
| */ |
| public void addFileInfo(int fileId, String fileName, |
| String absoluteFileName) throws AbsentInformationException { |
| if (fPrimaryFileId == -1) { |
| fPrimaryFileId = fileId; |
| } |
| FileInfo fileInfo = new FileInfo(fileId, fileName, absoluteFileName); |
| if (fFileInfos.contains(fileInfo)) { |
| throw new AbsentInformationException(NLS.bind( |
| JDIMessages.ReferenceTypeImpl_28, new String[] { |
| Integer.toString(fileId), fId })); |
| } |
| fFileInfos.add(fileInfo); |
| } |
| |
| /** |
| * Add line mapping information. |
| * |
| * @param inputStartLine |
| * number of the first line in the input source file. |
| * @param lineFileId |
| * id of the input source file. |
| * @param repeatCount |
| * number of iterations. |
| * @param outputStartLine |
| * number of the first line in the output source file. |
| * @param outputLineIncrement |
| * number of line to increment at each iteration. |
| * @throws AbsentInformationException |
| */ |
| public void addLineInfo(int inputStartLine, int lineFileId, |
| int repeatCount, int outputStartLine, int outputLineIncrement) |
| throws AbsentInformationException { |
| FileInfo fileInfo = null; |
| // get the FileInfo object |
| for (Iterator<FileInfo> iter = fFileInfos.iterator(); iter.hasNext();) { |
| FileInfo element = iter.next(); |
| if (element.fFileId == lineFileId) { |
| fileInfo = element; |
| } |
| } |
| if (fileInfo == null) { |
| throw new AbsentInformationException(NLS.bind( |
| JDIMessages.ReferenceTypeImpl_29, |
| new String[] { Integer.toString(lineFileId) })); |
| } |
| // add the data to the different hash maps. |
| for (int i = 0; i < repeatCount; i++, inputStartLine++) { |
| fileInfo.addLineInfo(inputStartLine, outputStartLine, |
| outputLineIncrement); |
| if (outputLineIncrement == 0) { |
| // see bug 40022 |
| addLineInfoToMap(inputStartLine, lineFileId, |
| outputStartLine); |
| } else { |
| for (int j = 0; j < outputLineIncrement; j++, outputStartLine++) { |
| addLineInfoToMap(inputStartLine, lineFileId, |
| outputStartLine); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Add the data to the map. |
| */ |
| private void addLineInfoToMap(int inputStartLine, int lineFileId, |
| int outputStartLine) { |
| Integer key = new Integer(outputStartLine); |
| List<int[]> inputLines = fOutputLineToInputLine.get(key); |
| if (inputLines == null) { |
| inputLines = new ArrayList<>(); |
| fOutputLineToInputLine.put(key, inputLines); |
| } |
| inputLines.add(new int[] { lineFileId, inputStartLine }); |
| } |
| |
| /** |
| * Return the FileInfo object for the specified source name. Return |
| * <code>null</code> if the specified name is the source name of no file |
| * info. |
| * |
| * @param sourceName |
| * the source name to search. |
| */ |
| public FileInfo getFileInfo(String sourceName) { |
| for (Iterator<FileInfo> iter = fFileInfos.iterator(); iter.hasNext();) { |
| FileInfo fileInfo = iter.next(); |
| if (fileInfo.fFileName.equals(sourceName)) { |
| return fileInfo; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param outputLineNumber |
| * @return |
| */ |
| public List<int[]> getInputLineInfos(int outputLineNumber) { |
| return fOutputLineToInputLine.get(new Integer( |
| outputLineNumber)); |
| } |
| |
| } |
| |
| /** ReferenceTypeID that corresponds to this reference. */ |
| private JdwpReferenceTypeID fReferenceTypeID; |
| |
| /** The following are the stored results of JDWP calls. */ |
| protected List<InterfaceType> fInterfaces = null; |
| private List<Method> fMethods = null; |
| private Map<JdwpMethodID, Method> fMethodTable = null; |
| private List<Field> fFields = null; |
| private List<Method> fAllMethods = null; |
| private List<Method> fVisibleMethods = null; |
| private List<Field> fAllFields = null; |
| private List<Field> fVisibleFields = null; |
| private List<InterfaceType> fAllInterfaces = null; |
| private Map<String, Map<String, List<Location>>> fStratumAllLineLocations = null; |
| private String fSourceName = null; |
| private int fModifierBits = -1; |
| private ClassLoaderReferenceImpl fClassLoader = null; |
| private ClassObjectReferenceImpl fClassObject = null; |
| |
| private String fGenericSignature; // 1.5 addition |
| private boolean fGenericSignatureKnown; // 1.5 addition |
| |
| private boolean fGotClassFileVersion = false; // HCR addition. |
| private int fClassFileVersion; // HCR addition. |
| private boolean fIsHCREligible; // HCR addition. |
| private boolean fIsVersionKnown; // HCR addition. |
| |
| private boolean fSourceDebugExtensionAvailable = true; // JSR-045 addition |
| |
| /** |
| * The default stratum id. |
| */ |
| private String fDefaultStratumId; // JSR-045 addition |
| |
| /** |
| * A map of the defined strata. Map stratum id -> Stratum object. (String -> |
| * Stratum). |
| */ |
| private Map<String, Stratum> fStrata; // JSR-045 addition |
| |
| /** |
| * The source map string returned by the VM. |
| */ |
| private String fSmap; // JSR-045 addition |
| |
| /** |
| * Creates new instance. |
| */ |
| protected ReferenceTypeImpl(String description, VirtualMachineImpl vmImpl, |
| JdwpReferenceTypeID referenceTypeID) { |
| super(description, vmImpl); |
| fReferenceTypeID = referenceTypeID; |
| } |
| |
| /** |
| * Creates new instance. |
| */ |
| protected ReferenceTypeImpl(String description, VirtualMachineImpl vmImpl, |
| JdwpReferenceTypeID referenceTypeID, String signature, |
| String genericSignature) { |
| super(description, vmImpl); |
| fReferenceTypeID = referenceTypeID; |
| setSignature(signature); |
| setGenericSignature(genericSignature); |
| } |
| |
| /** |
| * @return Returns type tag. |
| */ |
| public abstract byte typeTag(); |
| |
| /** |
| * Flushes all stored Jdwp results. |
| */ |
| public void flushStoredJdwpResults() { |
| // Flush Methods. |
| if (fMethods != null) { |
| for (Method method : fMethods) { |
| ((MethodImpl)method).flushStoredJdwpResults(); |
| } |
| fMethods = null; |
| fMethodTable = null; |
| } |
| |
| // Flush Fields. |
| if (fFields != null) { |
| for (Field field : fFields) { |
| ((FieldImpl)field).flushStoredJdwpResults(); |
| } |
| fFields = null; |
| } |
| |
| fInterfaces = null; |
| fAllMethods = null; |
| fVisibleMethods = null; |
| fAllFields = null; |
| fVisibleFields = null; |
| fAllInterfaces = null; |
| fStratumAllLineLocations = null; |
| fSourceName = null; |
| fModifierBits = -1; |
| fClassLoader = null; |
| fClassObject = null; |
| fGotClassFileVersion = false; |
| // java 1.5 |
| fGenericSignature = null; |
| fGenericSignatureKnown = false; |
| |
| // JSR-045 |
| fSourceDebugExtensionAvailable = true; |
| fDefaultStratumId = null; |
| fStrata = null; |
| fSmap = null; |
| |
| // The following cached results are stored higher up in the class |
| // hierarchy. |
| fSignature = null; |
| fSourceName = null; |
| } |
| |
| /** |
| * @return Returns the interfaces declared as implemented by this class. |
| * Interfaces indirectly implemented (extended by the implemented |
| * interface or implemented by a superclass) are not included. |
| */ |
| public List<InterfaceType> allInterfaces() { |
| if (fAllInterfaces != null) { |
| return fAllInterfaces; |
| } |
| |
| /* |
| * Recursion: The interfaces that it directly implements; All interfaces |
| * that are implemented by its interfaces; If it is a class, all |
| * interfaces that are implemented by its superclass. |
| */ |
| // The interfaces are maintained in a set, to avoid duplicates. |
| // The interfaces of its own (own interfaces() command) are first |
| // inserted. |
| HashSet<InterfaceType> allInterfacesSet = new HashSet<>(interfaces()); |
| |
| // All interfaces of the interfaces it implements. |
| Iterator<InterfaceType> interfaces = interfaces().iterator(); |
| InterfaceType inter; |
| while (interfaces.hasNext()) { |
| inter = interfaces.next(); |
| allInterfacesSet.addAll(((InterfaceTypeImpl)inter).allInterfaces()); |
| } |
| |
| // If it is a class, all interfaces of it's superclass. |
| if (this instanceof ClassType) { |
| ClassType superclass = ((ClassType) this).superclass(); |
| if (superclass != null) { |
| allInterfacesSet.addAll(superclass.allInterfaces()); |
| } |
| } |
| |
| fAllInterfaces = new ArrayList<>(allInterfacesSet); |
| return fAllInterfaces; |
| } |
| |
| /** |
| * @return Returns JDWP Reference ID. |
| */ |
| public JdwpReferenceTypeID getRefTypeID() { |
| return fReferenceTypeID; |
| } |
| |
| /** |
| * @return Returns modifier bits. |
| */ |
| @Override |
| public int modifiers() { |
| if (fModifierBits != -1) { |
| return fModifierBits; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_MODIFIERS, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fModifierBits = readInt( |
| "modifiers", AccessibleImpl.getModifierStrings(), replyData); //$NON-NLS-1$ |
| return fModifierBits; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Add methods to a set of methods if they are not overridden, add new |
| * names+signature combinations to set of names+signature combinations. |
| */ |
| private void addVisibleMethods(List<Method> inheritedMethods, Set<String> nameAndSignatures, List<Method> resultMethods) { |
| Iterator<Method> iter = inheritedMethods.iterator(); |
| Method inheritedMethod; |
| while (iter.hasNext()) { |
| inheritedMethod = iter.next(); |
| if (!nameAndSignatures.contains(inheritedMethod.name() |
| + inheritedMethod.signature())) { |
| resultMethods.add(inheritedMethod); |
| } |
| } |
| } |
| |
| /** |
| * @return Returns a list containing each visible and unambiguous Method in |
| * this type. |
| */ |
| @Override |
| public List<Method> visibleMethods() { |
| if (fVisibleMethods != null) { |
| return fVisibleMethods; |
| } |
| |
| /* |
| * Recursion: The methods of its own (own methods() command); All |
| * methods of the interfaces it implements; If it is a class, all |
| * methods of it's superclass. |
| */ |
| // The name+signature combinations of methods are maintained in a set, |
| // to avoid including methods that have been overridden. |
| Set<String> namesAndSignatures = new HashSet<>(); |
| List<Method> visibleMethods = new ArrayList<>(); |
| |
| // The methods of its own (own methods() command). |
| for (Iterator<Method> iter = methods().iterator(); iter.hasNext();) { |
| MethodImpl method = (MethodImpl) iter.next(); |
| namesAndSignatures.add(method.name() + method.signature()); |
| visibleMethods.add(method); |
| } |
| |
| // All methods of the interfaces it implements. |
| Iterator<InterfaceType> interfaces = interfaces().iterator(); |
| InterfaceType inter; |
| while (interfaces.hasNext()) { |
| inter = interfaces.next(); |
| addVisibleMethods(inter.visibleMethods(), namesAndSignatures, |
| visibleMethods); |
| } |
| |
| // If it is a class, all methods of it's superclass. |
| if (this instanceof ClassType) { |
| ClassType superclass = ((ClassType) this).superclass(); |
| if (superclass != null) { |
| addVisibleMethods(superclass.visibleMethods(), |
| namesAndSignatures, visibleMethods); |
| } |
| } |
| |
| fVisibleMethods = visibleMethods; |
| return fVisibleMethods; |
| } |
| |
| /** |
| * @return Returns a list containing each Method declared in this type, and |
| * its super-classes, implemented interfaces, and/or |
| * super-interfaces. |
| */ |
| @Override |
| public List<Method> allMethods() { |
| if (fAllMethods != null) { |
| return fAllMethods; |
| } |
| |
| /* |
| * Recursion: The methods of its own (own methods() command); All |
| * methods of the interfaces it implements; If it is a class, all |
| * methods of it's superclass. |
| */ |
| // The name+signature combinations of methods are maintained in a set. |
| HashSet<Method> resultSet = new HashSet<>(); |
| |
| // The methods of its own (own methods() command). |
| resultSet.addAll(methods()); |
| |
| // All methods of the interfaces it implements. |
| Iterator<InterfaceType> interfaces = interfaces().iterator(); |
| InterfaceType inter; |
| while (interfaces.hasNext()) { |
| inter = interfaces.next(); |
| resultSet.addAll(inter.allMethods()); |
| } |
| |
| // If it is a class, all methods of it's superclass. |
| if (this instanceof ClassType) { |
| ClassType superclass = ((ClassType) this).superclass(); |
| if (superclass != null) { |
| resultSet.addAll(superclass.allMethods()); |
| } |
| } |
| |
| fAllMethods = new ArrayList<>(resultSet); |
| return fAllMethods; |
| } |
| |
| /** |
| * @return Returns the interfaces declared as implemented by this class. |
| * Interfaces indirectly implemented (extended by the implemented |
| * interface or implemented by a superclass) are not included. |
| */ |
| public List<InterfaceType> interfaces() { |
| if (fInterfaces != null) { |
| return fInterfaces; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_INTERFACES, this); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.NOT_FOUND: |
| // Workaround for problem in J2ME WTK (wireless toolkit) |
| // @see Bug 12966 |
| return Collections.EMPTY_LIST; |
| default: |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| } |
| DataInputStream replyData = replyPacket.dataInStream(); |
| List<InterfaceType> elements = new ArrayList<>(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| for (int i = 0; i < nrOfElements; i++) { |
| InterfaceTypeImpl ref = InterfaceTypeImpl.read(this, replyData); |
| if (ref == null) { |
| continue; |
| } |
| elements.add(ref); |
| } |
| fInterfaces = elements; |
| return elements; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Add fields to a set of fields if they are not overridden, add new field |
| * names to set of field names. |
| */ |
| private void addVisibleFields(List<Field> newFields, Set<String> names, List<Field> resultFields) { |
| Iterator<Field> iter = newFields.iterator(); |
| FieldImpl field; |
| while (iter.hasNext()) { |
| field = (FieldImpl) iter.next(); |
| String name = field.name(); |
| if (!names.contains(name)) { |
| resultFields.add(field); |
| names.add(name); |
| } |
| } |
| } |
| |
| /** |
| * @return Returns a list containing each visible and unambiguous Field in |
| * this type. |
| */ |
| @Override |
| public List<Field> visibleFields() { |
| if (fVisibleFields != null) { |
| return fVisibleFields; |
| } |
| |
| /* |
| * Recursion: The fields of its own (own fields() command); All fields |
| * of the interfaces it implements; If it is a class, all fields of it's |
| * superclass. |
| */ |
| // The names of fields are maintained in a set, to avoid including |
| // fields that have been overridden. |
| HashSet<String> fieldNames = new HashSet<>(); |
| |
| // The fields of its own (own fields() command). |
| List<Field> visibleFields = new ArrayList<>(); |
| addVisibleFields(fields(), fieldNames, visibleFields); |
| |
| // All fields of the interfaces it implements. |
| Iterator<InterfaceType> interfaces = interfaces().iterator(); |
| InterfaceType inter; |
| while (interfaces.hasNext()) { |
| inter = interfaces.next(); |
| addVisibleFields(inter.visibleFields(), fieldNames, visibleFields); |
| } |
| |
| // If it is a class, all fields of it's superclass. |
| if (this instanceof ClassType) { |
| ClassType superclass = ((ClassType) this).superclass(); |
| if (superclass != null) { |
| addVisibleFields(superclass.visibleFields(), fieldNames, |
| visibleFields); |
| } |
| } |
| |
| fVisibleFields = visibleFields; |
| return fVisibleFields; |
| } |
| |
| /** |
| * @return Returns a list containing each Field declared in this type, and |
| * its super-classes, implemented interfaces, and/or |
| * super-interfaces. |
| */ |
| @Override |
| public List<Field> allFields() { |
| if (fAllFields != null) { |
| return fAllFields; |
| } |
| |
| /* |
| * Recursion: The fields of its own (own fields() command); All fields |
| * of the interfaces it implements; If it is a class, all fields of it's |
| * superclass. |
| */ |
| // The names of fields are maintained in a set, to avoid including |
| // fields that have been inherited double. |
| HashSet<Field> resultSet = new HashSet<>(); |
| |
| // The fields of its own (own fields() command). |
| resultSet.addAll(fields()); |
| |
| // All fields of the interfaces it implements. |
| Iterator<InterfaceType> interfaces = interfaces().iterator(); |
| InterfaceType inter; |
| while (interfaces.hasNext()) { |
| inter = interfaces.next(); |
| resultSet.addAll(inter.allFields()); |
| } |
| |
| // If it is a class, all fields of it's superclass. |
| if (this instanceof ClassType) { |
| ClassType superclass = ((ClassType) this).superclass(); |
| if (superclass != null) { |
| resultSet.addAll(superclass.allFields()); |
| } |
| } |
| |
| fAllFields = new ArrayList<>(resultSet); |
| return fAllFields; |
| } |
| |
| /** |
| * @return Returns the class loader object which loaded the class |
| * corresponding to this type. |
| */ |
| @Override |
| public ClassLoaderReference classLoader() { |
| if (fClassLoader != null) { |
| return fClassLoader; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CLASS_LOADER, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fClassLoader = ClassLoaderReferenceImpl.read(this, replyData); |
| return fClassLoader; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns the class object that corresponds to this type in the |
| * target VM. |
| */ |
| @Override |
| public ClassObjectReference classObject() { |
| if (fClassObject != null) { |
| return fClassObject; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CLASS_OBJECT, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fClassObject = ClassObjectReferenceImpl.read(this, replyData); |
| return fClassObject; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns status of class/interface. |
| */ |
| protected int status() { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_STATUS, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int status = readInt("status", classStatusStrings(), replyData); //$NON-NLS-1$ |
| return status; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns true if initialization failed for this class. |
| */ |
| @Override |
| public boolean failedToInitialize() { |
| return (status() & JDWP_CLASS_STATUS_ERROR) != 0; |
| } |
| |
| /** |
| * @return Returns true if this type has been initialized. |
| */ |
| @Override |
| public boolean isInitialized() { |
| return (status() & JDWP_CLASS_STATUS_INITIALIZED) != 0; |
| } |
| |
| /** |
| * @return Returns true if this type has been prepared. |
| */ |
| @Override |
| public boolean isPrepared() { |
| return (status() & JDWP_CLASS_STATUS_PREPARED) != 0; |
| } |
| |
| /** |
| * @return Returns true if this type has been verified. |
| */ |
| @Override |
| public boolean isVerified() { |
| return (status() & JDWP_CLASS_STATUS_VERIFIED) != 0; |
| } |
| |
| /** |
| * @return Returns the visible Field with the given non-ambiguous name. |
| */ |
| @Override |
| public Field fieldByName(String name) { |
| Iterator<Field> iter = visibleFields().iterator(); |
| while (iter.hasNext()) { |
| FieldImpl field = (FieldImpl) iter.next(); |
| if (field.name().equals(name)) { |
| return field; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return Returns a list containing each Field declared in this type. |
| */ |
| @Override |
| public List<Field> fields() { |
| if (fFields != null) { |
| return fFields; |
| } |
| |
| // Note: Fields are returned in the order they occur in the class file, |
| // therefore their |
| // order in this list can be used for comparisons. |
| initJdwpRequest(); |
| try { |
| boolean withGenericSignature = virtualMachineImpl() |
| .isJdwpVersionGreaterOrEqual(1, 5); |
| int jdwpCommand = withGenericSignature ? JdwpCommandPacket.RT_FIELDS_WITH_GENERIC |
| : JdwpCommandPacket.RT_FIELDS; |
| JdwpReplyPacket replyPacket = requestVM(jdwpCommand, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| List<Field> elements = new ArrayList<>(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| for (int i = 0; i < nrOfElements; i++) { |
| FieldImpl elt = FieldImpl.readWithNameSignatureModifiers(this, |
| this, withGenericSignature, replyData); |
| if (elt == null) { |
| continue; |
| } |
| elements.add(elt); |
| } |
| fFields = elements; |
| return fFields; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns FieldImpl of a field in the reference specified by a |
| * given fieldID, or null if not found. |
| */ |
| public FieldImpl findField(JdwpFieldID fieldID) { |
| Iterator<Field> iter = fields().iterator(); |
| while (iter.hasNext()) { |
| FieldImpl field = (FieldImpl) iter.next(); |
| if (field.getFieldID().equals(fieldID)) { |
| return field; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return Returns MethodImpl of a method in the reference specified by a |
| * given methodID, or null if not found. |
| */ |
| public Method findMethod(JdwpMethodID methodID) { |
| if (methodID.value() == 0) { |
| return new MethodImpl(virtualMachineImpl(), this, methodID, |
| JDIMessages.ReferenceTypeImpl_Obsolete_method_1, |
| "", null, -1); //$NON-NLS-1$ |
| } |
| if (fMethodTable == null) { |
| // 509259 use temporary variable to workaround fMethodTable lazy initialization race |
| Map<JdwpMethodID, Method> methodTable = new HashMap<>(); |
| Iterator<Method> iter = methods().iterator(); |
| while (iter.hasNext()) { |
| MethodImpl method = (MethodImpl) iter.next(); |
| methodTable.put(method.getMethodID(), method); |
| } |
| fMethodTable = Collections.unmodifiableMap(methodTable); |
| } |
| return fMethodTable.get(methodID); |
| } |
| |
| /** |
| * @return Returns the Value of a given static Field in this type. |
| */ |
| @Override |
| public Value getValue(Field field) { |
| ArrayList<Field> list = new ArrayList<>(1); |
| list.add(field); |
| return getValues(list).get(field); |
| } |
| |
| /** |
| * @return a Map of the requested static Field objects with their Value. |
| */ |
| @Override |
| public Map<Field, Value> getValues(List<? extends Field> fields) { |
| // if the field list is empty, nothing to do |
| if (fields.isEmpty()) { |
| return new HashMap<>(); |
| } |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| int fieldsSize = fields.size(); |
| write(this, outData); |
| writeInt(fieldsSize, "size", outData); //$NON-NLS-1$ |
| for (int i = 0; i < fieldsSize; i++) { |
| FieldImpl field = (FieldImpl) fields.get(i); |
| checkVM(field); |
| field.getFieldID().write(outData); |
| } |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_GET_VALUES, outBytes); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| HashMap<Field, Value> map = new HashMap<>(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| if (nrOfElements != fieldsSize) { |
| throw new InternalError( |
| JDIMessages.ReferenceTypeImpl_Retrieved_a_different_number_of_values_from_the_VM_than_requested_3); |
| } |
| |
| for (int i = 0; i < nrOfElements; i++) { |
| map.put(fields.get(i), ValueImpl.readWithTag(this, replyData)); |
| } |
| return map; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns the hash code value. |
| */ |
| @Override |
| public int hashCode() { |
| return fReferenceTypeID.hashCode(); |
| } |
| |
| /** |
| * @return Returns true if two mirrors refer to the same entity in the |
| * target VM. |
| * @see java.lang.Object#equals(Object) |
| */ |
| @Override |
| public boolean equals(Object object) { |
| return object != null |
| && object.getClass().equals(this.getClass()) |
| && fReferenceTypeID |
| .equals(((ReferenceTypeImpl) object).fReferenceTypeID) |
| && virtualMachine().equals( |
| ((MirrorImpl) object).virtualMachine()); |
| } |
| |
| /** |
| * @return Returns a negative integer, zero, or a positive integer as this |
| * {@link ReferenceType} is less than, equal to, or greater than the specified |
| * {@link ReferenceType}. |
| */ |
| @Override |
| public int compareTo(ReferenceType type) { |
| if (type == null || !type.getClass().equals(this.getClass())) { |
| throw new ClassCastException(JDIMessages.ReferenceTypeImpl_Can__t_compare_reference_type_to_given_object_4); |
| } |
| return name().compareTo(type.name()); |
| } |
| |
| /** |
| * @return Returns true if the type was declared abstract. |
| */ |
| @Override |
| public boolean isAbstract() { |
| return (modifiers() & MODIFIER_ACC_ABSTRACT) != 0; |
| } |
| |
| /** |
| * @return Returns true if the type was declared final. |
| */ |
| @Override |
| public boolean isFinal() { |
| return (modifiers() & MODIFIER_ACC_FINAL) != 0; |
| } |
| |
| /** |
| * @return Returns true if the type was declared static. |
| */ |
| @Override |
| public boolean isStatic() { |
| return (modifiers() & MODIFIER_ACC_STATIC) != 0; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#locationsOfLine(int) |
| */ |
| @Override |
| public List<Location> locationsOfLine(int line) throws AbsentInformationException { |
| return locationsOfLine(virtualMachine().getDefaultStratum(), null, line); |
| } |
| |
| /** |
| * @return Returns a list containing each Method declared directly in this |
| * type. |
| */ |
| @Override |
| public List<Method> methods() { |
| // Note that ArrayReference overwrites this method by returning an empty |
| // list. |
| if (fMethods != null) { |
| return fMethods; |
| } |
| |
| // Note: Methods are returned in the order they occur in the class file, |
| // therefore their |
| // order in this list can be used for comparisons. |
| initJdwpRequest(); |
| try { |
| boolean withGenericSignature = virtualMachineImpl() |
| .isJdwpVersionGreaterOrEqual(1, 5); |
| int jdwpCommand = withGenericSignature ? JdwpCommandPacket.RT_METHODS_WITH_GENERIC |
| : JdwpCommandPacket.RT_METHODS; |
| JdwpReplyPacket replyPacket = requestVM(jdwpCommand, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| List<Method> elements = new ArrayList<>(); |
| int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$ |
| for (int i = 0; i < nrOfElements; i++) { |
| MethodImpl elt = MethodImpl.readWithNameSignatureModifiers( |
| this, this, withGenericSignature, replyData); |
| if (elt == null) { |
| continue; |
| } |
| elements.add(elt); |
| } |
| fMethods = elements; |
| return fMethods; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns a List containing each visible Method that has the given |
| * name. |
| */ |
| @Override |
| public List<Method> methodsByName(String name) { |
| List<Method> elements = new ArrayList<>(); |
| Iterator<Method> iter = visibleMethods().iterator(); |
| while (iter.hasNext()) { |
| Method method = iter.next(); |
| if (method.name().equals(name)) { |
| elements.add(method); |
| } |
| } |
| return elements; |
| } |
| |
| /** |
| * @return Returns a List containing each visible Method that has the given |
| * name and signature. |
| */ |
| @Override |
| public List<Method> methodsByName(String name, String signature) { |
| List<Method> elements = new ArrayList<>(); |
| Iterator<Method> iter = visibleMethods().iterator(); |
| while (iter.hasNext()) { |
| MethodImpl method = (MethodImpl) iter.next(); |
| if (method.name().equals(name) && method.signature().equals(signature)) { |
| elements.add(method); |
| } |
| } |
| return elements; |
| } |
| |
| /** |
| * @return Returns the fully qualified name of this type. |
| */ |
| @Override |
| public String name() { |
| // Make sure that we know the signature, from which the name is derived. |
| if (fName == null) { |
| setName(signatureToName(signature())); |
| } |
| return fName; |
| } |
| |
| /** |
| * @return Returns the JNI-style signature for this type. |
| */ |
| @Override |
| public String signature() { |
| if (fSignature != null) { |
| return fSignature; |
| } |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_SIGNATURE, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| setSignature(readString("signature", replyData)); //$NON-NLS-1$ |
| return fSignature; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns a List containing each ReferenceType declared within this |
| * type. |
| */ |
| @Override |
| public List<ReferenceType> nestedTypes() { |
| // Note that the VM gives an empty reply on RT_NESTED_TYPES, therefore |
| // we search for the |
| // nested types in the loaded types. |
| List<ReferenceType> result = new ArrayList<>(); |
| Iterator<ReferenceType> itr = virtualMachineImpl().allRefTypes(); |
| while (itr.hasNext()) { |
| try { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl) itr.next(); |
| String refName = refType.name(); |
| if (refName.length() > name().length() |
| && refName.startsWith(name()) |
| && refName.charAt(name().length()) == '$') { |
| result.add(refType); |
| } |
| } catch (ClassNotPreparedException e) { |
| continue; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @return Returns an identifying name for the source corresponding to the |
| * declaration of this type. |
| */ |
| @Override |
| public String sourceName() throws AbsentInformationException { |
| // sourceNames list in never empty, an AbsentInformationException is |
| // thrown |
| // if the source name is not known. |
| return sourceNames(virtualMachine().getDefaultStratum()) |
| .get(0); |
| } |
| |
| /** |
| * @return Returns the CRC-32 of the given reference type, undefined if |
| * unknown. |
| */ |
| @Override |
| public int getClassFileVersion() { |
| virtualMachineImpl().checkHCRSupported(); |
| if (fGotClassFileVersion) { |
| return fClassFileVersion; |
| } |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.HCR_GET_CLASS_VERSION, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fIsHCREligible = readBoolean("HCR eligible", replyData); //$NON-NLS-1$ |
| fIsVersionKnown = readBoolean("version known", replyData); //$NON-NLS-1$ |
| fClassFileVersion = readInt("class file version", replyData); //$NON-NLS-1$ |
| fGotClassFileVersion = true; |
| return fClassFileVersion; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns whether the CRC-32 of the given reference type is known. |
| */ |
| @Override |
| public boolean isVersionKnown() { |
| getClassFileVersion(); |
| return fIsVersionKnown; |
| } |
| |
| /** |
| * @return Returns whether the reference type is HCR-eligible. |
| */ |
| @Override |
| public boolean isHCREligible() { |
| getClassFileVersion(); |
| return fIsHCREligible; |
| } |
| |
| /** |
| * Writes JDWP representation. |
| */ |
| public void write(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| fReferenceTypeID.write(out); |
| if (target.fVerboseWriter != null) |
| { |
| target.fVerboseWriter.println( |
| "referenceType", fReferenceTypeID.value()); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Writes representation of null referenceType. |
| */ |
| public static void writeNull(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| // create null id |
| JdwpReferenceTypeID ID = new JdwpReferenceTypeID( |
| target.virtualMachineImpl()); |
| ID.write(out); |
| if (target.fVerboseWriter != null) |
| { |
| target.fVerboseWriter.println("referenceType", ID.value()); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Writes JDWP representation. |
| */ |
| public void writeWithTag(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| target.writeByte(typeTag(), "type tag", JdwpID.typeTagMap(), out); //$NON-NLS-1$ |
| write(target, out); |
| } |
| |
| /** |
| * @return Reads JDWP representation and returns new or cached instance. |
| */ |
| public static ReferenceTypeImpl readWithTypeTag(MirrorImpl target, |
| DataInputStream in) throws IOException { |
| byte typeTag = target.readByte("type tag", JdwpID.typeTagMap(), in); //$NON-NLS-1$ |
| switch (typeTag) { |
| case 0: |
| return null; |
| case ArrayTypeImpl.typeTag: |
| return ArrayTypeImpl.read(target, in); |
| case ClassTypeImpl.typeTag: |
| return ClassTypeImpl.read(target, in); |
| case InterfaceTypeImpl.typeTag: |
| return InterfaceTypeImpl.read(target, in); |
| } |
| throw new InternalException( |
| JDIMessages.ReferenceTypeImpl_Invalid_ReferenceTypeID_tag_encountered___8 |
| + typeTag); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#allLineLocations() |
| */ |
| @Override |
| public List<Location> allLineLocations() throws AbsentInformationException { |
| return allLineLocations(virtualMachine().getDefaultStratum(), null); |
| } |
| |
| /** |
| * @return Reads JDWP representation and returns new or cached instance. |
| */ |
| public static ReferenceTypeImpl readWithTypeTagAndSignature( |
| MirrorImpl target, boolean withGenericSignature, DataInputStream in) |
| throws IOException { |
| byte typeTag = target.readByte("type tag", JdwpID.typeTagMap(), in); //$NON-NLS-1$ |
| switch (typeTag) { |
| case 0: |
| return null; |
| case ArrayTypeImpl.typeTag: |
| return ArrayTypeImpl.readWithSignature(target, |
| withGenericSignature, in); |
| case ClassTypeImpl.typeTag: |
| return ClassTypeImpl.readWithSignature(target, |
| withGenericSignature, in); |
| case InterfaceTypeImpl.typeTag: |
| return InterfaceTypeImpl.readWithSignature(target, |
| withGenericSignature, in); |
| } |
| throw new InternalException( |
| JDIMessages.ReferenceTypeImpl_Invalid_ReferenceTypeID_tag_encountered___8 |
| + typeTag); |
| } |
| |
| /** |
| * @return Returns new instance based on signature and classLoader. |
| * @throws ClassNotLoadedException |
| * when the ReferenceType has not been loaded by the specified |
| * class loader. |
| */ |
| public static TypeImpl create(VirtualMachineImpl vmImpl, String signature, |
| ClassLoaderReference classLoader) throws ClassNotLoadedException { |
| ReferenceTypeImpl refTypeBootstrap = null; |
| List<ReferenceType> classes = vmImpl.classesBySignature(signature); |
| ReferenceTypeImpl type; |
| Iterator<ReferenceType> iter = classes.iterator(); |
| while (iter.hasNext()) { |
| // First pass. Look for a class loaded by the given class loader |
| type = (ReferenceTypeImpl) iter.next(); |
| if (type.classLoader() == null) { // bootstrap classloader |
| if (classLoader == null) { |
| return type; |
| } |
| refTypeBootstrap = type; |
| } |
| if (classLoader != null && classLoader.equals(type.classLoader())) { |
| return type; |
| } |
| } |
| // If no ReferenceType is found with the specified classloader, but |
| // there is one with the |
| // bootstrap classloader, the latter is returned. |
| if (refTypeBootstrap != null) { |
| return refTypeBootstrap; |
| } |
| |
| List<ReferenceType> visibleTypes; |
| iter = classes.iterator(); |
| while (iter.hasNext()) { |
| // Second pass. Look for a class that is visible to |
| // the given class loader |
| type = (ReferenceTypeImpl) iter.next(); |
| visibleTypes = classLoader.visibleClasses(); |
| Iterator<ReferenceType> visibleIter = visibleTypes.iterator(); |
| while (visibleIter.hasNext()) { |
| if (type.equals(visibleIter.next())) { |
| return type; |
| } |
| } |
| } |
| |
| throw new ClassNotLoadedException(classSignatureToName(signature), |
| JDIMessages.ReferenceTypeImpl_Type_has_not_been_loaded_10); |
| } |
| |
| /** |
| * Retrieves constant mappings. |
| */ |
| public static void getConstantMaps() { |
| if (fgClassStatusStrings != null) { |
| return; |
| } |
| |
| java.lang.reflect.Field[] fields = ReferenceTypeImpl.class |
| .getDeclaredFields(); |
| fgClassStatusStrings = new String[32]; |
| |
| for (java.lang.reflect.Field field : fields) { |
| if ((field.getModifiers() & Modifier.PUBLIC) == 0 |
| || (field.getModifiers() & Modifier.STATIC) == 0 |
| || (field.getModifiers() & Modifier.FINAL) == 0) { |
| continue; |
| } |
| |
| String name = field.getName(); |
| if (!name.startsWith("JDWP_CLASS_STATUS_")) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| name = name.substring(18); |
| |
| try { |
| int value = field.getInt(null); |
| |
| for (int j = 0; j < fgClassStatusStrings.length; j++) { |
| if ((1 << j & value) != 0) { |
| fgClassStatusStrings[j] = name; |
| break; |
| } |
| } |
| } catch (IllegalAccessException e) { |
| // Will not occur for own class. |
| } catch (IllegalArgumentException e) { |
| // Should not occur. |
| // We should take care that all public static final constants |
| // in this class are numbers that are convertible to int. |
| } |
| } |
| } |
| |
| /** |
| * @return Returns a map with string representations of tags. |
| */ |
| public static String[] classStatusStrings() { |
| getConstantMaps(); |
| return fgClassStatusStrings; |
| } |
| |
| /** |
| * @see TypeImpl#createNullValue() |
| */ |
| @Override |
| public Value createNullValue() { |
| return null; |
| } |
| |
| /** |
| * @see ReferenceType#sourceNames(String) |
| */ |
| @Override |
| public List<String> sourceNames(String stratumId) throws AbsentInformationException { |
| List<String> list = new ArrayList<>(); |
| Stratum stratum = getStratum(stratumId); |
| if (stratum != null) { |
| // return the source names defined for this stratum in the SMAP. |
| List<FileInfo> fileInfos = stratum.fFileInfos; |
| if (fileInfos.isEmpty()) { |
| throw new AbsentInformationException( |
| JDIMessages.ReferenceTypeImpl_30); |
| } |
| for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) { |
| list.add(iter.next().fFileName); |
| } |
| return list; |
| } |
| // Java stratum |
| if (fSourceName == null) { |
| getSourceName(); |
| } |
| list.add(fSourceName); |
| return list; |
| } |
| |
| /** |
| * @see ReferenceType#sourcePaths(String) |
| */ |
| @Override |
| public List<String> sourcePaths(String stratumId) throws AbsentInformationException { |
| List<String> list = new ArrayList<>(); |
| Stratum stratum = getStratum(stratumId); |
| if (stratum != null) { |
| // return the source paths defined for this stratum in the SMAP. |
| for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) { |
| FileInfo fileInfo = iter.next(); |
| String path = fileInfo.fAbsoluteFileName; |
| if (path == null) { |
| path = getPath(fileInfo.fFileName); |
| } |
| list.add(path); |
| } |
| return list; |
| } |
| // Java stratum |
| if (fSourceName == null) { |
| getSourceName(); |
| } |
| list.add(getPath(fSourceName)); |
| return list; |
| } |
| |
| /** |
| * @see ReferenceType#sourceDebugExtension() |
| */ |
| @Override |
| public String sourceDebugExtension() throws AbsentInformationException { |
| if (isSourceDebugExtensionAvailable()) { |
| return fSmap; |
| } |
| if (!virtualMachine().canGetSourceDebugExtension()) { |
| throw new UnsupportedOperationException("1"); //$NON-NLS-1$ |
| } |
| throw new AbsentInformationException(); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#allLineLocations(java.lang.String, java.lang.String) |
| */ |
| @Override |
| public List<Location> allLineLocations(String stratum, String sourceName) throws AbsentInformationException { |
| Iterator<Method> allMethods = methods().iterator(); |
| if (stratum == null) { // if stratum not defined use the default stratum |
| stratum = defaultStratum(); |
| } |
| List<Location> allLineLocations = null; |
| Map<String, List<Location>> sourceNameAllLineLocations = null; |
| if (fStratumAllLineLocations == null) { // the stratum map doesn't |
| // exist, create it |
| fStratumAllLineLocations = new HashMap<>(); |
| } else { |
| // get the source name map |
| sourceNameAllLineLocations = fStratumAllLineLocations.get(stratum); |
| } |
| if (sourceNameAllLineLocations == null) { // the source name map doesn't |
| // exist, create it |
| sourceNameAllLineLocations = new HashMap<>(); |
| fStratumAllLineLocations.put(stratum, sourceNameAllLineLocations); |
| } else { |
| // get the line locations |
| allLineLocations = sourceNameAllLineLocations.get(sourceName); |
| } |
| if (allLineLocations == null) { // the line locations are not known, compute and store them |
| allLineLocations = new ArrayList<>(); |
| boolean hasLineInformation = false; |
| AbsentInformationException exception = null; |
| while (allMethods.hasNext()) { |
| MethodImpl method = (MethodImpl) allMethods.next(); |
| if (method.isAbstract() || method.isNative()) { |
| continue; |
| } |
| try { |
| allLineLocations.addAll(method.allLineLocations(stratum, sourceName)); |
| hasLineInformation = true; |
| } catch (AbsentInformationException e) { |
| exception = e; |
| } |
| } |
| if (!hasLineInformation && exception != null) { |
| throw exception; |
| } |
| sourceNameAllLineLocations.put(sourceName, allLineLocations); |
| } |
| return allLineLocations; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#locationsOfLine(java.lang.String, java.lang.String, int) |
| */ |
| @Override |
| public List<Location> locationsOfLine(String stratum, String sourceName, int lineNumber) throws AbsentInformationException { |
| Iterator<Method> allMethods = methods().iterator(); |
| List<Location> locations = new ArrayList<>(); |
| boolean hasLineInformation = false; |
| AbsentInformationException exception = null; |
| while (allMethods.hasNext()) { |
| MethodImpl method = (MethodImpl) allMethods.next(); |
| if (method.isAbstract() || method.isNative()) { |
| continue; |
| } |
| // one line in the input source can be translate in multiple lines |
| // in different |
| // methods in the output source. We need all these locations. |
| try { |
| locations.addAll(locationsOfLine(stratum, sourceName, lineNumber, method)); |
| hasLineInformation = true; |
| } catch (AbsentInformationException e) { |
| exception = e; |
| } |
| } |
| if (!hasLineInformation && exception != null) { |
| throw exception; |
| } |
| return locations; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#availableStrata() |
| */ |
| @Override |
| public List<String> availableStrata() { |
| List<String> list = new ArrayList<>(); |
| // The strata defined in the SMAP. |
| if (isSourceDebugExtensionAvailable()) { |
| list.addAll(fStrata.keySet()); |
| } |
| // plus the Java stratum |
| list.add(VirtualMachineImpl.JAVA_STRATUM_NAME); |
| return list; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#defaultStratum() |
| */ |
| @Override |
| public String defaultStratum() { |
| if (isSourceDebugExtensionAvailable()) { |
| return fDefaultStratumId; |
| } |
| // if not defined, return Java. |
| return VirtualMachineImpl.JAVA_STRATUM_NAME; |
| } |
| |
| /** |
| * Generate a source path from the given source name. The returned string is |
| * the package name of this type converted to a platform dependent path |
| * followed by the given source name. For example, on a Unix platform, the |
| * type org.my.TestJsp with the source name test.jsp would return |
| * "org/my/test.jsp". |
| */ |
| private String getPath(String sourceName) { |
| String name = name(); |
| int lastDotOffset = name.lastIndexOf('.'); |
| if (lastDotOffset == -1) { |
| return sourceName; |
| } |
| char fileSeparator = System.getProperty("file.separator").charAt(0); //$NON-NLS-1$ |
| return name.substring(0, lastDotOffset).replace('.', fileSeparator) |
| + fileSeparator + sourceName; |
| } |
| |
| /** |
| * Return the stratum object for this stratum Id. If the the specified |
| * stratum id is not defined for this reference type, return the stratum |
| * object for the default stratum. If the specified stratum id (or the |
| * default stratum id, if the specified stratum id is not defined) is |
| * <code>Java</code>, return <code>null</code>. |
| */ |
| private Stratum getStratum(String stratumId) { |
| if (!VirtualMachineImpl.JAVA_STRATUM_NAME.equals(stratumId) |
| && isSourceDebugExtensionAvailable()) { |
| if (stratumId == null || !fStrata.keySet().contains(stratumId)) { |
| stratumId = fDefaultStratumId; |
| } |
| if (!VirtualMachineImpl.JAVA_STRATUM_NAME.equals(stratumId)) { |
| return fStrata.get(stratumId); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get the source debug extension from the VM. |
| * |
| * @throws AbsentInformationException |
| */ |
| private void getSourceDebugExtension() throws AbsentInformationException { |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_SOURCE_DEBUG_EXTENSION, this); |
| if (replyPacket.errorCode() == JdwpReplyPacket.ABSENT_INFORMATION) { |
| throw new AbsentInformationException( |
| JDIMessages.ReferenceTypeImpl_31); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fSmap = readString(JDIMessages.ReferenceTypeImpl_32, replyData); |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| // TODO: remove the workaround when the J9SC20030415 bug is fixed (see |
| // bug 96485 of the vendor bug system). |
| // Workaround to a J9SC bug. It returns an empty string instead of a |
| // ABSENT_INFORMATION |
| // error if the source debug extension is not available. |
| if ("".equals(fSmap)) { //$NON-NLS-1$ |
| throw new AbsentInformationException( |
| JDIMessages.ReferenceTypeImpl_31); |
| } |
| // parse the source map. |
| fStrata = new HashMap<>(); |
| SourceDebugExtensionParser.parse(fSmap, this); |
| } |
| |
| /** |
| * Get the name of the Java source file from the VM. |
| * |
| * @throws AbsentInformationException |
| */ |
| private void getSourceName() throws AbsentInformationException { |
| if (fSourceName != null || isSourceDebugExtensionAvailable()) { |
| return; |
| } |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_SOURCE_FILE, this); |
| if (replyPacket.errorCode() == JdwpReplyPacket.ABSENT_INFORMATION) { |
| throw new AbsentInformationException( |
| JDIMessages.ReferenceTypeImpl_Source_name_is_not_known_7); |
| } |
| |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fSourceName = readString("source name", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Check in the source debug extension is available. To call before doing |
| * operations which need data from the SMAP. Return <code>false</code> if |
| * the source debug extension is not available for any reason. |
| * <code>true</code> indicates that the source debug extension is available |
| * and the information has been parsed and stored in the maps and lists. |
| */ |
| private synchronized boolean isSourceDebugExtensionAvailable() { |
| if (!fSourceDebugExtensionAvailable) { |
| return false; |
| } |
| if (!virtualMachine().canGetSourceDebugExtension()) { |
| fSourceDebugExtensionAvailable = false; |
| return false; |
| } |
| if (fSmap == null) { |
| try { |
| getSourceDebugExtension(); |
| } catch (AbsentInformationException e) { |
| fSourceDebugExtensionAvailable = false; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Set the output file name, i.e. the .java file used to generate the |
| * bytecode. |
| */ |
| protected void setOutputFileName(String outputFileName) { |
| fSourceName = outputFileName; |
| } |
| |
| /** |
| * Set the default stratum. This stratum will be used for the method on |
| * strata related data, but with no stratum parameter. |
| */ |
| protected void setDefaultStratumId(String defaultStratumId) { |
| fDefaultStratumId = defaultStratumId; |
| } |
| |
| /** |
| * Add a new stratum to this type. |
| */ |
| protected void addStratum(Stratum stratum) { |
| fStrata.put(stratum.fId, stratum); |
| } |
| |
| /** |
| * Return the name of the input source file of which the given code index is |
| * part of the translation, for this stratum. If the code at the given index |
| * is not a part of the translation of the given stratum code, return the |
| * name of the primary input source file. |
| * |
| * @param codeIndex |
| * the index of the code. |
| * @param method |
| * the method where is the code. |
| * @param stratumId |
| */ |
| protected String sourceName(long codeIndex, MethodImpl method, |
| String stratumId) throws AbsentInformationException { |
| Stratum stratum = getStratum(stratumId); |
| if (stratum != null) { |
| FileInfo fileInfo = fileInfo(codeIndex, method, stratum); |
| if (fileInfo != null) { |
| return fileInfo.fFileName; |
| } |
| } |
| // Java stratum |
| if (fSourceName == null) { |
| getSourceName(); |
| } |
| return fSourceName; |
| } |
| |
| /** |
| * Return the FileInfo object of the input source file of which the given |
| * code index is part of the translation, for this stratum. If the code at |
| * the given index is not a part of the translation of the given stratum |
| * code, return the FileInfo of the primary input source file. |
| * |
| * @param codeIndex |
| * the index of the code. |
| * @param method |
| * the method where is the code. |
| * @param stratum |
| */ |
| private FileInfo fileInfo(long codeIndex, MethodImpl method, Stratum stratum) { |
| int fileId = stratum.fPrimaryFileId; |
| if (stratum.fFileInfos.size() > 1) { |
| List<int[]> lineInfos = null; |
| try { |
| lineInfos = lineInfos(codeIndex, method, stratum); |
| } catch (AbsentInformationException e) { |
| // nothing to do, use the primary file id. |
| } |
| if (lineInfos != null) { |
| fileId = lineInfos.get(0)[0]; |
| } |
| } |
| for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext();) { |
| FileInfo fileInfo = iter.next(); |
| if (fileInfo.fFileId == fileId) { |
| return fileInfo; |
| } |
| } |
| // should never return null |
| return null; |
| } |
| |
| /** |
| * Return the list of line number in the input files of the stratum |
| * associated with the code at the given address. |
| * |
| * @param codeIndex |
| * the index of the code. |
| * @param method |
| * the method where is the code. |
| * @param stratum |
| * @return List of int[2]: [fileId, inputLineNumber] |
| */ |
| private List<int[]> lineInfos(long codeIndex, MethodImpl method, Stratum stratum) throws AbsentInformationException { |
| int outputLineNumber = -1; |
| try { |
| outputLineNumber = method.javaStratumLineNumber(codeIndex); |
| } catch (NativeMethodException e) { // Occurs in SUN VM. |
| return null; |
| } |
| if (outputLineNumber != -1) { |
| return stratum.getInputLineInfos(outputLineNumber); |
| } |
| return null; |
| } |
| |
| /** |
| * Return the path of the input source file of which the given code index is |
| * part of the translation, for this stratum. If the code at the given index |
| * is not a part of the translation of the given stratum code, return the |
| * path of the primary input source file. |
| * |
| * @param codeIndex |
| * the index of the code. |
| * @param method |
| * the method where is the code. |
| * @param stratumId |
| */ |
| protected String sourcePath(long codeIndex, MethodImpl method, |
| String stratumId) throws AbsentInformationException { |
| Stratum stratum = getStratum(stratumId); |
| if (stratum != null) { |
| FileInfo fileInfo = fileInfo(codeIndex, method, stratum); |
| if (fileInfo != null) { |
| String path = fileInfo.fAbsoluteFileName; |
| if (path == null) { |
| return getPath(fileInfo.fFileName); |
| } |
| return path; |
| } |
| } |
| // Java stratum |
| if (fSourceName == null) { |
| getSourceName(); |
| } |
| return getPath(fSourceName); |
| } |
| |
| /** |
| * Return the number of the line of which the given code index is part of |
| * the translation, for this stratum. |
| * |
| * @param codeIndex |
| * the index of the code. |
| * @param method |
| * the method where is the code. |
| * @param stratumId |
| */ |
| protected int lineNumber(long codeIndex, MethodImpl method, String stratumId) { |
| Stratum stratum = getStratum(stratumId); |
| try { |
| if (stratum != null) { |
| List<int[]> lineInfos = lineInfos(codeIndex, method, stratum); |
| if (lineInfos != null) { |
| return lineInfos.get(0)[1]; |
| } |
| return LocationImpl.LINE_NR_NOT_AVAILABLE; |
| } |
| // Java stratum |
| try { |
| return method.javaStratumLineNumber(codeIndex); |
| } catch (NativeMethodException e) { // Occurs in SUN VM. |
| return LocationImpl.LINE_NR_NOT_AVAILABLE; |
| } |
| } catch (AbsentInformationException e) { |
| return LocationImpl.LINE_NR_NOT_AVAILABLE; |
| } |
| } |
| |
| /** |
| * Return the location which are part of the translation of the given line, |
| * in the given stratum in the source file with the given source name. If |
| * sourceName is <code>null</code>, return the locations for all source file |
| * in the given stratum. The returned location are in the given method. |
| * |
| * @param stratumId |
| * the stratum id. |
| * @param sourceName |
| * the name of the source file. |
| * @param lineNumber |
| * the number of the line. |
| * @param method |
| * @throws AbsentInformationException |
| * if the specified sourceName is not valid. |
| */ |
| public List<Location> locationsOfLine(String stratumId, String sourceName, int lineNumber, MethodImpl method) throws AbsentInformationException { |
| Stratum stratum = getStratum(stratumId); |
| List<Integer> javaLines = new ArrayList<>(); |
| if (stratum != null) { |
| boolean found = false; |
| for (Iterator<FileInfo> iter = stratum.fFileInfos.iterator(); iter.hasNext() && !found;) { |
| FileInfo fileInfo = iter.next(); |
| if (sourceName == null || (found = sourceName.equals(fileInfo.fFileName))) { |
| javaLines.addAll(fileInfo.getOutputLinesForLine(lineNumber)); |
| } |
| } |
| if (sourceName != null && !found) { |
| throw new AbsentInformationException(JDIMessages.ReferenceTypeImpl_34); |
| } |
| } else { // Java stratum |
| javaLines.add(new Integer(lineNumber)); |
| } |
| return method.javaStratumLocationsOfLines(javaLines); |
| } |
| |
| /** |
| * Return the locations of all lines in the given source file of the given |
| * stratum which are included in the given method. If sourceName is |
| * <code>null</code>, return the locations for all source file in the given |
| * stratum. |
| * |
| * @param stratumId |
| * the stratum id |
| * @param sourceName |
| * the name of the source file |
| * @param method |
| * @param codeIndexTable |
| * the list of code indexes for the method, as get from the |
| * VM/JDWP |
| * @param javaStratumLineNumberTable |
| * the list of line numbers in the java stratum for the method, |
| * as get from the VM/JDWP |
| * @return |
| */ |
| public List<Location> allLineLocations(String stratumId, String sourceName, |
| MethodImpl method, long[] codeIndexTable, |
| int[] javaStratumLineNumberTable) throws AbsentInformationException { |
| Stratum stratum = getStratum(stratumId); |
| if (stratum != null) { |
| int[][] lineInfoTable = new int[codeIndexTable.length][]; |
| if (sourceName == null) { |
| int lastIndex = 0; |
| for (int i = 0, length = javaStratumLineNumberTable.length; i < length; i++) { |
| // for each executable line in the java source, get the |
| // associated lines in the stratum source |
| List<int[]> lineInfos = stratum.getInputLineInfos(javaStratumLineNumberTable[i]); |
| if (lineInfos != null) { |
| int[] lineInfo = lineInfos.get(0); |
| if (!lineInfo.equals(lineInfoTable[lastIndex])) { |
| lineInfoTable[i] = lineInfo; |
| lastIndex = i; |
| } |
| } |
| } |
| } else { // sourceName != null |
| FileInfo fileInfo = stratum.getFileInfo(sourceName); |
| if (fileInfo == null) { |
| throw new AbsentInformationException(JDIMessages.ReferenceTypeImpl_34); |
| } |
| int fileId = fileInfo.fFileId; |
| int lastIndex = 0; |
| for (int i = 0, length = javaStratumLineNumberTable.length; i < length; i++) { |
| List<int[]> lineInfos = stratum.getInputLineInfos(javaStratumLineNumberTable[i]); |
| if (lineInfos != null) { |
| for (Iterator<int[]> iter = lineInfos.iterator(); iter.hasNext();) { |
| int[] lineInfo = iter.next(); |
| if (lineInfo[0] == fileId) { |
| if (!lineInfo.equals(lineInfoTable[lastIndex])) { |
| lineInfoTable[i] = lineInfo; |
| lastIndex = i; |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| List<Location> locations = new ArrayList<>(); |
| for (int i = 0, length = lineInfoTable.length; i < length; i++) { |
| if (lineInfoTable[i] != null) { |
| locations.add(new LocationImpl(virtualMachineImpl(), method, codeIndexTable[i])); |
| } |
| } |
| return locations; |
| } |
| // Java stratum |
| List<Location> result = new ArrayList<>(); |
| for (long element : codeIndexTable) { |
| result.add(new LocationImpl(virtualMachineImpl(), method, element)); |
| } |
| return result; |
| } |
| |
| /* |
| * @since 3.0 |
| * |
| * @since java 1.5 |
| */ |
| @Override |
| public String genericSignature() { |
| if (fGenericSignatureKnown) { |
| return fGenericSignature; |
| } |
| if (virtualMachineImpl().isJdwpVersionGreaterOrEqual(1, 5)) { |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_SIGNATURE_WITH_GENERIC, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| setSignature(readString("signature", replyData)); //$NON-NLS-1$ |
| fGenericSignature = readString("generic signature", replyData); //$NON-NLS-1$ |
| if (fGenericSignature.length() == 0) { |
| fGenericSignature = null; |
| } |
| fGenericSignatureKnown = true; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } else { |
| fGenericSignatureKnown = true; |
| } |
| return fGenericSignature; |
| } |
| |
| /** |
| * if genericSignature is <code>null</code>, the generic signature is set to |
| * not-known (genericSignature() will ask the VM for the generic signature) |
| * if genericSignature is an empty String, the generic signature is set to |
| * no-generic-signature (genericSignature() will return null) if |
| * genericSignature is an non-empty String, the generic signature is set to |
| * the specified value (genericSignature() will return the specified value) |
| * |
| * @since 3.0 |
| */ |
| public void setGenericSignature(String genericSignature) { |
| if (genericSignature == null) { |
| fGenericSignature = null; |
| fGenericSignatureKnown = false; |
| } else { |
| if (genericSignature.length() == 0) { |
| fGenericSignature = null; |
| } else { |
| fGenericSignature = genericSignature; |
| } |
| fGenericSignatureKnown = true; |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ReferenceType#instances(long) |
| */ |
| @Override |
| public List<ObjectReference> instances(long maxInstances) { |
| try { |
| int max = (int) maxInstances; |
| if (maxInstances >= Integer.MAX_VALUE) { |
| max = Integer.MAX_VALUE; |
| } |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| write(this, outData); |
| writeInt(max, "max instances", outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_INSTANCES, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_OBJECT: |
| case JdwpReplyPacket.INVALID_CLASS: |
| throw new ObjectCollectedException( |
| JDIMessages.class_or_object_not_known); |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException( |
| JDIMessages.ReferenceTypeImpl_27); |
| case JdwpReplyPacket.ILLEGAL_ARGUMENT: |
| throw new IllegalArgumentException( |
| JDIMessages.ReferenceTypeImpl_26); |
| case JdwpReplyPacket.VM_DEAD: |
| throw new VMDisconnectedException(JDIMessages.vm_dead); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| int elements = readInt("element count", replyData); //$NON-NLS-1$ |
| if (max > 0 && elements > max) { |
| elements = max; |
| } |
| ArrayList<ObjectReference> list = new ArrayList<>(); |
| for (int i = 0; i < elements; i++) { |
| list.add((ObjectReference) ValueImpl.readWithTag(this, replyData)); |
| } |
| return list; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @see com.sun.jdi.ReferenceType#majorVersion() |
| * @since 3.3 |
| */ |
| @Override |
| public int majorVersion() { |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| getRefTypeID().write(outData); |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CLASS_VERSION, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_CLASS: |
| case JdwpReplyPacket.INVALID_OBJECT: |
| throw new ObjectCollectedException( |
| JDIMessages.class_or_object_not_known); |
| case JdwpReplyPacket.ABSENT_INFORMATION: |
| return 0; |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException( |
| JDIMessages.ReferenceTypeImpl_no_class_version_support24); |
| case JdwpReplyPacket.VM_DEAD: |
| throw new VMDisconnectedException(JDIMessages.vm_dead); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| return readInt("major version", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @see com.sun.jdi.ReferenceType#minorVersion() |
| * @since 3.3 |
| */ |
| @Override |
| public int minorVersion() { |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| getRefTypeID().write(outData); |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CLASS_VERSION, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_CLASS: |
| case JdwpReplyPacket.INVALID_OBJECT: |
| throw new ObjectCollectedException( |
| JDIMessages.class_or_object_not_known); |
| case JdwpReplyPacket.ABSENT_INFORMATION: |
| return 0; |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException( |
| JDIMessages.ReferenceTypeImpl_no_class_version_support24); |
| case JdwpReplyPacket.VM_DEAD: |
| throw new VMDisconnectedException(JDIMessages.vm_dead); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| readInt("major version", replyData); //$NON-NLS-1$ |
| return readInt("minor version", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @see com.sun.jdi.ReferenceType#constantPoolCount() |
| * @since 3.3 |
| */ |
| @Override |
| public int constantPoolCount() { |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| this.getRefTypeID().write(outData); |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CONSTANT_POOL, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_CLASS: |
| case JdwpReplyPacket.INVALID_OBJECT: |
| throw new ObjectCollectedException( |
| JDIMessages.class_or_object_not_known); |
| case JdwpReplyPacket.ABSENT_INFORMATION: |
| return 0; |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException( |
| JDIMessages.ReferenceTypeImpl_no_constant_pool_support); |
| case JdwpReplyPacket.VM_DEAD: |
| throw new VMDisconnectedException(JDIMessages.vm_dead); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| return readInt("pool count", replyData); //$NON-NLS-1$ |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return 0; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @see com.sun.jdi.ReferenceType#constantPool() |
| * @since 3.3 |
| */ |
| @Override |
| public byte[] constantPool() { |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| this.getRefTypeID().write(outData); |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.RT_CONSTANT_POOL, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_CLASS: |
| case JdwpReplyPacket.INVALID_OBJECT: |
| throw new ObjectCollectedException( |
| JDIMessages.class_or_object_not_known); |
| case JdwpReplyPacket.ABSENT_INFORMATION: |
| return new byte[0]; |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException( |
| JDIMessages.ReferenceTypeImpl_no_constant_pool_support); |
| case JdwpReplyPacket.VM_DEAD: |
| throw new VMDisconnectedException(JDIMessages.vm_dead); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| |
| DataInputStream replyData = replyPacket.dataInStream(); |
| readInt("pool count", replyData); //$NON-NLS-1$ |
| int bytes = readInt("byte count", replyData); //$NON-NLS-1$ |
| byte[] array = new byte[bytes]; |
| for (int i = 0; i < bytes; i++) { |
| array[i] = readByte("byte read", replyData); //$NON-NLS-1$ |
| } |
| return array; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * @return Returns Jdwp version of given options. |
| */ |
| protected int optionsToJdwpOptions(int options) { |
| int jdwpOptions = 0; |
| if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) { |
| jdwpOptions |= MethodImpl.INVOKE_SINGLE_THREADED_JDWP; |
| } |
| return jdwpOptions; |
| } |
| |
| |
| /** |
| * Invoke static method on class or interface type |
| * |
| * @param thread the debugger thread in which to invoke |
| * @param method the resolved chosed Method to invoke |
| * @param arguments the list of Values to supply as arguments for the method, assigned to arguments in the order they appear in the method signature. |
| * @param options the integer bit flags |
| * @param command the JWDP command to invoke |
| * @return a Value representing the method's return value. |
| * @throws InvalidTypeException If the arguments do not match |
| * @throws ClassNotLoadedException if any argument type has not yet been loaded in the VM |
| * @throws IncompatibleThreadStateException if the specified thread has not been suspended |
| * @throws InvocationException if the method invocation resulted in an exception |
| */ |
| protected Value invokeMethod(ThreadReference thread, Method method, List<? extends Value> arguments, int options, int command) throws InvalidTypeException, |
| ClassNotLoadedException, IncompatibleThreadStateException, |
| InvocationException { |
| checkVM(thread); |
| checkVM(method); |
| ThreadReferenceImpl threadImpl = (ThreadReferenceImpl) thread; |
| MethodImpl methodImpl = (MethodImpl) method; |
| |
| // Perform some checks for IllegalArgumentException. |
| if (!visibleMethods().contains(method)) { |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Class_does_not_contain_given_method_1); |
| } |
| if (method.argumentTypeNames().size() != arguments.size()) { |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Number_of_arguments_doesn__t_match_2); |
| } |
| if (method.isConstructor() || method.isStaticInitializer()) { |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Method_is_constructor_or_intitializer_3); |
| } |
| |
| // check the type and the VM of the arguments. Convert the values if |
| // needed |
| List<Value> checkedArguments = ValueImpl.checkValues(arguments, method.argumentTypes(), virtualMachineImpl()); |
| |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| write(this, outData); |
| threadImpl.write(this, outData); |
| methodImpl.write(this, outData); |
| |
| writeInt(checkedArguments.size(), "size", outData); //$NON-NLS-1$ |
| Iterator<Value> iter = checkedArguments.iterator(); |
| while (iter.hasNext()) { |
| Value elt = iter.next(); |
| if (elt instanceof ValueImpl) { |
| ((ValueImpl)elt).writeWithTag(this, outData); |
| } else { |
| ValueImpl.writeNullWithTag(this, outData); |
| } |
| } |
| |
| writeInt(optionsToJdwpOptions(options), |
| "options", MethodImpl.getInvokeOptions(), outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| command, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.INVALID_METHODID: |
| throw new IllegalArgumentException(); |
| case JdwpReplyPacket.TYPE_MISMATCH: |
| throw new InvalidTypeException(); |
| case JdwpReplyPacket.INVALID_CLASS: |
| throw new ClassNotLoadedException(name()); |
| case JdwpReplyPacket.INVALID_THREAD: |
| throw new IncompatibleThreadStateException(); |
| case JdwpReplyPacket.THREAD_NOT_SUSPENDED: |
| throw new IncompatibleThreadStateException(); |
| case JdwpReplyPacket.NOT_IMPLEMENTED: |
| throw new UnsupportedOperationException(JDIMessages.InterfaceTypeImpl_Static_interface_methods_require_newer_JVM); |
| } |
| |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| ValueImpl value = ValueImpl.readWithTag(this, replyData); |
| ObjectReferenceImpl exception = ObjectReferenceImpl |
| .readObjectRefWithTag(this, replyData); |
| if (exception != null) { |
| throw new InvocationException(exception); |
| } |
| return value; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| } |