/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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.Hashtable; 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 Hashtable<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) { fMethodTable = new Hashtable<>(); Iterator<Method> iter = methods().iterator(); while (iter.hasNext()) { MethodImpl method = (MethodImpl) iter.next(); fMethodTable.put(method.getMethodID(), method); } } 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(); } } }