/*******************************************************************************
 * 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
 *******************************************************************************/
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.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdi.Bootstrap;
import org.eclipse.jdi.internal.connect.PacketReceiveManager;
import org.eclipse.jdi.internal.connect.PacketSendManager;
import org.eclipse.jdi.internal.event.EventQueueImpl;
import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket;
import org.eclipse.jdi.internal.jdwp.JdwpObjectID;
import org.eclipse.jdi.internal.jdwp.JdwpReferenceTypeID;
import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket;
import org.eclipse.jdi.internal.request.EventRequestManagerImpl;
import org.eclipse.osgi.util.NLS;

import com.sun.jdi.BooleanValue;
import com.sun.jdi.ByteValue;
import com.sun.jdi.CharValue;
import com.sun.jdi.DoubleValue;
import com.sun.jdi.FloatValue;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.LongValue;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ShortValue;
import com.sun.jdi.StringReference;
import com.sun.jdi.ThreadGroupReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VoidValue;
import com.sun.jdi.connect.spi.Connection;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.request.EventRequestManager;

/**
 * This class implements the corresponding interfaces declared by the JDI
 * specification. See the com.sun.jdi package for more information.
 *
 */
public class VirtualMachineImpl extends MirrorImpl implements VirtualMachine,
		org.eclipse.jdi.hcr.VirtualMachine, org.eclipse.jdi.VirtualMachine {
	/** Result flags for Classes Have Changed command. */
	public static final byte HCR_RELOAD_SUCCESS = 0;
	public static final byte HCR_RELOAD_FAILURE = 1;
	public static final byte HCR_RELOAD_IGNORED = 2;

	/* Indexes in HCR capabilities list. */
	private static final int HCR_CAN_RELOAD_CLASSES = 0;
	private static final int HCR_CAN_GET_CLASS_VERSION = 1;
	private static final int HCR_CAN_DO_RETURN = 2;
	private static final int HCR_CAN_REENTER_ON_EXIT = 3;

	protected static final String JAVA_STRATUM_NAME = "Java"; //$NON-NLS-1$

	/** Timeout value for requests to VM if not overridden for a particular VM. */
	private int fRequestTimeout;
	/** Mapping of command codes to strings. */

	private static Map<Integer, String> fgHCRResultMap = null;

	/** EventRequestManager that creates event objects on request. */
	private EventRequestManagerImpl fEventReqMgr;
	/** EventQueue that returns EventSets from the Virtual Manager. */
	private EventQueueImpl fEventQueue;

	/** If a launching connector is used, we store the process. */
	private Process fLaunchedProcess;

	/**
	 * The following field contains cached Mirrors. Note that these are
	 * optional: their only purpose is to speed up the debugger by being able to
	 * use the stored results of JDWP calls.
	 */
	private ValueCache fCachedReftypes = new ValueCache();
	private ValueCache fCachedObjects = new ValueCache();

	/** The following are the stored results of JDWP calls. */
	private String fVersionDescription = null; // Text information on the VM
												// version.
	private int fJdwpMajorVersion;
	private int fJdwpMinorVersion;
	private String fVMVersion; // Target VM JRE version, as in the java.version
								// property.
	private String fVMName; // Target VM name, as in the java.vm.name property.
	private boolean fGotIDSizes = false;
	private int fFieldIDSize;
	private int fMethodIDSize;
	private int fObjectIDSize;
	private int fReferenceTypeIDSize;
	private int fFrameIDSize;

	private boolean fGotCapabilities = false;
	private boolean fCanWatchFieldModification;
	private boolean fCanWatchFieldAccess;
	private boolean fCanGetBytecodes;
	private boolean fCanGetSyntheticAttribute;
	private boolean fCanGetOwnedMonitorInfo;
	private boolean fCanGetCurrentContendedMonitor;
	private boolean fCanGetMonitorInfo;
	private boolean fCanRedefineClasses;
	private boolean fCanAddMethod;
	private boolean fCanUnrestrictedlyRedefineClasses;
	private boolean fCanPopFrames;
	private boolean fCanUseInstanceFilters;
	private boolean fCanGetSourceDebugExtension;
	private boolean fCanRequestVMDeathEvent;
	private boolean fCanSetDefaultStratum;
	private boolean fCanGetInstanceInfo;
	private boolean fCanGetConstantPool;
	private boolean fCanUseSourceNameFilters;
	private boolean fCanForceEarlyReturn;
	private boolean fCanRequestMonitorEvents;
	private boolean fCanGetMonitorFrameInfo;
	private boolean[] fHcrCapabilities = null;

	/*
	 * singletons for primitive types
	 */
	private BooleanTypeImpl fBooleanType;
	private ByteTypeImpl fByteType;
	private CharTypeImpl fCharType;
	private DoubleTypeImpl fDoubleType;
	private FloatTypeImpl fFloatType;
	private IntegerTypeImpl fIntegerType;
	private LongTypeImpl fLongType;
	private ShortTypeImpl fShortType;

	/**
	 * Disconnected flag
	 */
	private boolean fIsDisconnected = false;

	/**
	 * The name of the current default stratum.
	 */
	private String fDefaultStratum;
	private PacketReceiveManager fPacketReceiveManager;
	private PacketSendManager fPacketSendManager;

	/**
	 * Creates a new Virtual Machine.
	 */
	public VirtualMachineImpl(Connection connection) {
		super("VirtualMachine"); //$NON-NLS-1$
		fEventReqMgr = new EventRequestManagerImpl(this);
		fEventQueue = new EventQueueImpl(this);
		fRequestTimeout = ((VirtualMachineManagerImpl) Bootstrap
				.virtualMachineManager()).getGlobalRequestTimeout();

		fPacketReceiveManager = new PacketReceiveManager(connection, this);
		Thread receiveThread = new Thread(fPacketReceiveManager,
				JDIMessages.VirtualMachineImpl_0);
		receiveThread.setDaemon(true);
		fPacketReceiveManager.setPartnerThread(receiveThread);
		receiveThread.start();

		fPacketSendManager = new PacketSendManager(connection);
		Thread sendThread = new Thread(fPacketSendManager,
				JDIMessages.VirtualMachineImpl_1);
		sendThread.setDaemon(true);
		fPacketReceiveManager.setPartnerThread(sendThread);
		sendThread.start();
	}

	/**
	 * @return Returns size of JDWP ID.
	 */
	public final int fieldIDSize() {
		return fFieldIDSize;
	}

	/**
	 * @return Returns size of JDWP ID.
	 */
	public final int methodIDSize() {
		return fMethodIDSize;
	}

	/**
	 * @return Returns size of JDWP ID.
	 */
	public final int objectIDSize() {
		return fObjectIDSize;
	}

	/**
	 * @return Returns size of JDWP ID.
	 */
	public final int referenceTypeIDSize() {
		return fReferenceTypeIDSize;
	}

	/**
	 * @return Returns size of JDWP ID.
	 */
	public final int frameIDSize() {
		return fFrameIDSize;
	}

	/**
	 * @return Returns cached mirror object, or null if method is not in cache.
	 */
	public ReferenceTypeImpl getCachedMirror(JdwpReferenceTypeID ID) {
		return (ReferenceTypeImpl) fCachedReftypes.get(ID);
	}

	/**
	 * @return Returns cached mirror object, or null if method is not in cache.
	 */
	public ObjectReferenceImpl getCachedMirror(JdwpObjectID ID) {
		return (ObjectReferenceImpl) fCachedObjects.get(ID);
	}

	/**
	 * Adds mirror object to cache.
	 */
	public void addCachedMirror(ReferenceTypeImpl mirror) {
		fCachedReftypes.put(mirror.getRefTypeID(), mirror);
		// TBD: It is now yet possible to only ask for unload events for
		// classes that we know of due to a limitation in the J9 VM.
		// eventRequestManagerImpl().enableInternalClasUnloadEvent(mirror);
	}

	/**
	 * Adds mirror object to cache.
	 */
	public void addCachedMirror(ObjectReferenceImpl mirror) {
		fCachedObjects.put(mirror.getObjectID(), mirror);
	}

	/**
	 * Flushes all stored Jdwp results.
	 */
	public void flushStoredJdwpResults() {
		// All known classes also become invalid.
		Iterator<Object> iter = fCachedReftypes.values().iterator();
		while (iter.hasNext()) {
			ReferenceTypeImpl refType = (ReferenceTypeImpl) iter.next();
			refType.flushStoredJdwpResults();
		}

		fVersionDescription = null;
		fGotIDSizes = false;
		fHcrCapabilities = null;
	}

	/*
	 * Removes a known class. A class/interface is known if we have ever
	 * received its ReferenceTypeID and we have not received an unload event for
	 * it.
	 */
	public final void removeKnownRefType(String signature) {
		List<ReferenceType> refTypeList = classesBySignature(signature);
		if (refTypeList.isEmpty()) {
			return;
		}

		// If we have only one known class for this signature, we known that
		// this is the class
		// to be removed.
		if (refTypeList.size() == 1) {
			ReferenceTypeImpl refType = (ReferenceTypeImpl) refTypeList.get(0);
			refType.flushStoredJdwpResults();
			fCachedReftypes.remove(refType.getRefTypeID());
			return;
		}

		// We have more than one known class for the signature, let's find the
		// unloaded one(s).
		Iterator<ReferenceType> iter = refTypeList.iterator();
		while (iter.hasNext()) {
			ReferenceTypeImpl refType = (ReferenceTypeImpl) iter.next();
			boolean prepared = false;
			try {
				prepared = refType.isPrepared();
			} catch (ObjectCollectedException exception) {
				// The type is unloaded. Fall through
			}
			if (!prepared) {
				refType.flushStoredJdwpResults();
				iter.remove();
				fCachedReftypes.remove(refType.getRefTypeID());
			}
		}
	}

	/*
	 * @exception Throws UnsupportedOperationException if VM does not support J9
	 * HCR.
	 */
	public void checkHCRSupported() throws UnsupportedOperationException {
		if (!isHCRSupported()) {
			throw new UnsupportedOperationException(
					NLS.bind(JDIMessages.VirtualMachineImpl_Target_VM__0__does_not_support_Hot_Code_Replacement_1, new String[] { name() }));
		}
	}

	/*
	 * Returns whether J9 HCR is supported
	 */
	public boolean isHCRSupported() throws UnsupportedOperationException {
		return name().equals("j9"); //$NON-NLS-1$
	}

	/*
	 * @return Returns Manager for receiving packets from the Virtual Machine.
	 */
	public final PacketReceiveManager packetReceiveManager() {
		return fPacketReceiveManager;
	}

	/*
	 * @return Returns Manager for sending packets to the Virtual Machine.
	 */
	public final PacketSendManager packetSendManager() {
		/*
		 * Before we send out first bytes to the VM by JDI calls, we need some
		 * initial requests: - Get the sizes of the IDs (fieldID, method ID
		 * etc.) that the VM uses; - Request class prepare and unload events. We
		 * used these to cache classes/interfaces and map their signatures.
		 */
		if (!fGotIDSizes) {
			getIDSizes();
			if (!fGotIDSizes) { // We can't do much without them.
				disconnectVM();
				throw new VMDisconnectedException(
						JDIMessages.VirtualMachineImpl_Failed_to_get_ID_sizes_2);
			}

			// TBD: This call should be moved to addKnownRefType() when it can
			// be made specific
			// for a reference type.
			eventRequestManagerImpl().enableInternalClasUnloadEvent();
		}

		return fPacketSendManager;
	}

	/**
	 * Returns all loaded types (classes, interfaces, and array types). For each
	 * loaded type in the target VM a ReferenceType will be placed in the
	 * returned list.
	 */
	@Override
	public List<ReferenceType> allClasses() {
		// Note that this information should not be cached.
		initJdwpRequest();
		try {
			boolean withGenericSignature = virtualMachineImpl()
					.isJdwpVersionGreaterOrEqual(1, 5);
			int jdwpCommand = withGenericSignature ? JdwpCommandPacket.VM_ALL_CLASSES_WITH_GENERIC
					: JdwpCommandPacket.VM_ALL_CLASSES;
			JdwpReplyPacket replyPacket = requestVM(jdwpCommand);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();
			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
			List<ReferenceType> elements = new ArrayList<>(nrOfElements);
			for (int i = 0; i < nrOfElements; i++) {
				ReferenceTypeImpl elt = ReferenceTypeImpl
						.readWithTypeTagAndSignature(this,
								withGenericSignature, replyData);
				if (elt == null) {
					continue;
				}
				readInt("status", ReferenceTypeImpl.classStatusStrings(), replyData); //$NON-NLS-1$
				elements.add(elt);
			}
			return elements;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}

	}

	/**
	 * @return Returns an iterator over all loaded classes.
	 */
	protected final Iterator<ReferenceType> allRefTypes() {
		return allClasses().iterator();
	}

	/**
	 * @return Returns an iterator over all cached classes.
	 */
	protected final Iterator<Object> allCachedRefTypes() {
		return fCachedReftypes.values().iterator();
	}

	/**
	 * Returns a list of the currently running threads. For each running thread
	 * in the target VM, a ThreadReference that mirrors it is placed in the
	 * list.
	 */
	@Override
	public List<ThreadReference> allThreads() {
		// Note that this information should not be cached.
		initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_ALL_THREADS);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();
			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
			List<ThreadReference> elements = new ArrayList<>(nrOfElements);
			for (int i = 0; i < nrOfElements; i++) {
				ThreadReferenceImpl elt = ThreadReferenceImpl.read(this,
						replyData);
				if (elt == null) {
					continue;
				}
				elements.add(elt);
			}
			return elements;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/**
	 * Retrieve this VM's capabilities.
	 */
	public void getCapabilities() {
		if (fGotCapabilities) {
			return;
		}

		int command = JdwpCommandPacket.VM_CAPABILITIES;
		if (isJdwpVersionGreaterOrEqual(1, 4)) {
			command = JdwpCommandPacket.VM_CAPABILITIES_NEW;
		}

		initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = requestVM(command);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();

			fCanWatchFieldModification = readBoolean(
					"watch field modification", replyData); //$NON-NLS-1$
			fCanWatchFieldAccess = readBoolean("watch field access", replyData); //$NON-NLS-1$
			fCanGetBytecodes = readBoolean("get bytecodes", replyData); //$NON-NLS-1$
			fCanGetSyntheticAttribute = readBoolean("synth. attr", replyData); //$NON-NLS-1$
			fCanGetOwnedMonitorInfo = readBoolean(
					"owned monitor info", replyData); //$NON-NLS-1$
			fCanGetCurrentContendedMonitor = readBoolean(
					"curr. contended monitor", replyData); //$NON-NLS-1$
			fCanGetMonitorInfo = readBoolean("monitor info", replyData); //$NON-NLS-1$
			if (command == JdwpCommandPacket.VM_CAPABILITIES_NEW) {
				// extended capabilities
				fCanRedefineClasses = readBoolean("redefine classes", replyData); //$NON-NLS-1$
				fCanAddMethod = readBoolean("add method", replyData); //$NON-NLS-1$
				fCanUnrestrictedlyRedefineClasses = readBoolean(
						"unrestrictedly redefine classes", replyData); //$NON-NLS-1$
				fCanPopFrames = readBoolean("pop frames", replyData); //$NON-NLS-1$
				fCanUseInstanceFilters = readBoolean(
						"use instance filters", replyData); //$NON-NLS-1$
				fCanGetSourceDebugExtension = readBoolean(
						"get source debug extension", replyData); //$NON-NLS-1$
				fCanRequestVMDeathEvent = readBoolean(
						"request vm death", replyData); //$NON-NLS-1$
				fCanSetDefaultStratum = readBoolean(
						"set default stratum", replyData); //$NON-NLS-1$
				fCanGetInstanceInfo = readBoolean("instance info", replyData); //$NON-NLS-1$
				fCanRequestMonitorEvents = readBoolean(
						"request monitor events", replyData); //$NON-NLS-1$
				fCanGetMonitorFrameInfo = readBoolean(
						"monitor frame info", replyData); //$NON-NLS-1$
				fCanUseSourceNameFilters = readBoolean(
						"source name filters", replyData); //$NON-NLS-1$
				fCanGetConstantPool = readBoolean("constant pool", replyData); //$NON-NLS-1$
				fCanForceEarlyReturn = readBoolean(
						"force early return", replyData); //$NON-NLS-1$
			} else {
				fCanRedefineClasses = false;
				fCanAddMethod = false;
				fCanUnrestrictedlyRedefineClasses = false;
				fCanPopFrames = false;
				fCanUseInstanceFilters = false;
				fCanGetSourceDebugExtension = false;
				fCanRequestVMDeathEvent = false;
				fCanSetDefaultStratum = false;
				fCanGetInstanceInfo = false;
				fCanGetConstantPool = false;
				fCanUseSourceNameFilters = false;
				fCanForceEarlyReturn = false;
				fCanRequestMonitorEvents = false;
				fCanGetMonitorFrameInfo = false;
			}
			fGotCapabilities = true;
		} catch (IOException e) {
			fGotIDSizes = false;
			defaultIOExceptionHandler(e);
		} finally {
			handledJdwpRequest();
		}
	}

	/**
	 * @see com.sun.jdi.VirtualMachine#canForceEarlyReturn()
	 * @since 3.3
	 */
	@Override
	public boolean canForceEarlyReturn() {
		getCapabilities();
		return fCanForceEarlyReturn;
	}

	/**
	 * @return Returns true if this implementation supports the retrieval of a
	 *         method's bytecodes.
	 */
	@Override
	public boolean canGetBytecodes() {
		getCapabilities();
		return fCanGetBytecodes;
	}

	/**
	 * @return Returns true if this implementation supports the retrieval of the
	 *         monitor for which a thread is currently waiting.
	 */
	@Override
	public boolean canGetCurrentContendedMonitor() {
		getCapabilities();
		return fCanGetCurrentContendedMonitor;
	}

	/**
	 * @see com.sun.jdi.VirtualMachine#canGetInstanceInfo()
	 * @since 3.3
	 */
	@Override
	public boolean canGetInstanceInfo() {
		getCapabilities();
		return fCanGetInstanceInfo;
	}

	/**
	 * @see com.sun.jdi.VirtualMachine#canGetMethodReturnValues()
	 * @since 3.3
	 */
	@Override
	public boolean canGetMethodReturnValues() {
		return isJdwpVersionGreaterOrEqual(1, 6);
	}

	/**
	 * @return Returns true if this implementation supports the retrieval of the
	 *         monitor information for an object.
	 */
	@Override
	public boolean canGetMonitorInfo() {
		getCapabilities();
		return fCanGetMonitorInfo;
	}

	/**
	 * @see com.sun.jdi.VirtualMachine#canGetMonitorFrameInfo()
	 * @since 3.3
	 */
	@Override
	public boolean canGetMonitorFrameInfo() {
		getCapabilities();
		return fCanGetMonitorFrameInfo;
	}

	/**
	 * @return Returns true if this implementation supports the retrieval of the
	 *         monitors owned by a thread.
	 */
	@Override
	public boolean canGetOwnedMonitorInfo() {
		getCapabilities();
		return fCanGetOwnedMonitorInfo;
	}

	/**
	 * @return Returns true if this implementation supports the query of the
	 *         synthetic attribute of a method or field.
	 */
	@Override
	public boolean canGetSyntheticAttribute() {
		getCapabilities();
		return fCanGetSyntheticAttribute;
	}

	/**
	 * @see com.sun.jdi.VirtualMachine#canRequestMonitorEvents()
	 * @since 3.3
	 */
	@Override
	public boolean canRequestMonitorEvents() {
		getCapabilities();
		return fCanRequestMonitorEvents;
	}

	/**
	 * @return Returns true if this implementation supports watchpoints for
	 *         field access.
	 */
	@Override
	public boolean canWatchFieldAccess() {
		getCapabilities();
		return fCanWatchFieldAccess;
	}

	/**
	 * @return Returns true if this implementation supports watchpoints for
	 *         field modification.
	 */
	@Override
	public boolean canWatchFieldModification() {
		getCapabilities();
		return fCanWatchFieldModification;
	}

	/**
	 * @return Returns the loaded reference types that match a given signature.
	 */
	public List<ReferenceType> classesBySignature(String signature) {
		// Note that this information should not be cached.
		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeString(signature, "signature", outData); //$NON-NLS-1$

			JdwpReplyPacket replyPacket = requestVM(
					JdwpCommandPacket.VM_CLASSES_BY_SIGNATURE, outBytes);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();
			int nrOfElements = readInt("elements", replyData); //$NON-NLS-1$
			List<ReferenceType> elements = new ArrayList<>(nrOfElements);
			for (int i = 0; i < nrOfElements; i++) {
				ReferenceTypeImpl elt = ReferenceTypeImpl.readWithTypeTag(this,
						replyData);
				readInt("status", ReferenceTypeImpl.classStatusStrings(), replyData); //$NON-NLS-1$
				if (elt == null) {
					continue;
				}
				elements.add(elt);
			}
			return elements;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#classesByName(java.lang.String)
	 */
	@Override
	public List<ReferenceType> classesByName(String name) {
		String signature = TypeImpl.classNameToSignature(name);
		return classesBySignature(signature);
	}

	/**
	 * Invalidates this virtual machine mirror.
	 */
	@Override
	public void dispose() {
		initJdwpRequest();
		try {
			requestVM(JdwpCommandPacket.VM_DISPOSE);
			disconnectVM();
		} catch (VMDisconnectedException e) {
			// The VM can exit before we receive the reply.
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#eventQueue()
	 */
	@Override
	public EventQueue eventQueue() {
		return fEventQueue;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#eventRequestManager()
	 */
	@Override
	public EventRequestManager eventRequestManager() {
		return fEventReqMgr;
	}

	/**
	 * @return Returns EventRequestManagerImpl that creates all event objects on
	 *         request.
	 */
	public EventRequestManagerImpl eventRequestManagerImpl() {
		return fEventReqMgr;
	}

	/**
	 * Causes the mirrored VM to terminate with the given error code.
	 */
	@Override
	public void exit(int exitCode) {
		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeInt(exitCode, "exit code", outData); //$NON-NLS-1$
			requestVM(JdwpCommandPacket.VM_EXIT, outBytes);
			disconnectVM();
		} catch (VMDisconnectedException e) {
			// The VM can exit before we receive the reply.
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(byte)
	 */
	@Override
	public ByteValue mirrorOf(byte value) {
		return new ByteValueImpl(virtualMachineImpl(), Byte.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(char)
	 */
	@Override
	public CharValue mirrorOf(char value) {
		return new CharValueImpl(virtualMachineImpl(), Character.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(double)
	 */
	@Override
	public DoubleValue mirrorOf(double value) {
		return new DoubleValueImpl(virtualMachineImpl(), Double.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(float)
	 */
	@Override
	public FloatValue mirrorOf(float value) {
		return new FloatValueImpl(virtualMachineImpl(), Float.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(int)
	 */
	@Override
	public IntegerValue mirrorOf(int value) {
		return new IntegerValueImpl(virtualMachineImpl(), Integer.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(long)
	 */
	@Override
	public LongValue mirrorOf(long value) {
		return new LongValueImpl(virtualMachineImpl(), Long.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(short)
	 */
	@Override
	public ShortValue mirrorOf(short value) {
		return new ShortValueImpl(virtualMachineImpl(), Short.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(boolean)
	 */
	@Override
	public BooleanValue mirrorOf(boolean value) {
		return new BooleanValueImpl(virtualMachineImpl(),
				Boolean.valueOf(value));
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOf(java.lang.String)
	 */
	@Override
	public StringReference mirrorOf(String value) {
		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeString(value, "string value", outData); //$NON-NLS-1$

			JdwpReplyPacket replyPacket = requestVM(
					JdwpCommandPacket.VM_CREATE_STRING, outBytes);
			defaultReplyErrorHandler(replyPacket.errorCode());

			DataInputStream replyData = replyPacket.dataInStream();
			StringReference result = StringReferenceImpl.read(this, replyData);
			return result;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#mirrorOfVoid()
	 */
	@Override
	public VoidValue mirrorOfVoid() {
		return new VoidValueImpl(this);
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#process()
	 */
	@Override
	public Process process() {
		return fLaunchedProcess;
	}

	/**
	 * Sets Process object for this virtual machine if launched by a
	 * LaunchingConnector.
	 */
	public void setLaunchedProcess(Process proc) {
		fLaunchedProcess = proc;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#resume()
	 */
	@Override
	public void resume() {
		initJdwpRequest();
		try {
			resetThreadEventFlags();
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_RESUME);
			defaultReplyErrorHandler(replyPacket.errorCode());
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#setDebugTraceMode(int)
	 */
	@Override
	public void setDebugTraceMode(int traceFlags) {
		// We don't have trace info.
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#suspend()
	 */
	@Override
	public void suspend() {
		initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_SUSPEND);
			defaultReplyErrorHandler(replyPacket.errorCode());
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#topLevelThreadGroups()
	 */
	@Override
	public List<ThreadGroupReference> topLevelThreadGroups() {
		// Note that this information should not be cached.
		initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_TOP_LEVEL_THREAD_GROUPS);
			defaultReplyErrorHandler(replyPacket.errorCode());

			DataInputStream replyData = replyPacket.dataInStream();
			int nrGroups = readInt("nr of groups", replyData); //$NON-NLS-1$
			ArrayList<ThreadGroupReference> result = new ArrayList<>(nrGroups);
			for (int i = 0; i < nrGroups; i++) {
				ThreadGroupReferenceImpl threadGroup = ThreadGroupReferenceImpl.read(this, replyData);
				result.add(threadGroup);
			}
			return result;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#name()
	 */
	@Override
	public String name() {
		getVersionInfo();
		return fVMName;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#version()
	 */
	@Override
	public String version() {
		getVersionInfo();
		return fVMVersion;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#description()
	 */
	@Override
	public String description() {
		getVersionInfo();
		return fVersionDescription;
	}

	/**
	 * Reset event flags of all threads.
	 */
	private void resetThreadEventFlags() {
		Iterator<ThreadReference> iter = allThreads().iterator();
		ThreadReferenceImpl thread;
		while (iter.hasNext()) {
			thread = (ThreadReferenceImpl) iter.next();
			thread.resetEventFlags();
		}
	}

	/**
	 * Request and fetch ID sizes of Virtual Machine.
	 */
	private void getIDSizes() {
		if (fGotIDSizes) {
			return;
		}

		/*
		 * fGotIDSizes must first be assigned true to prevent an infinite loop
		 * because getIDSizes() calls requestVM which calls packetSendManager.
		 */
		fGotIDSizes = true;

		// We use a different mirror to avoid having verbose output mixed with
		// the initiating command.
		MirrorImpl mirror = new VoidValueImpl(this);

		mirror.initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = mirror
					.requestVM(JdwpCommandPacket.VM_ID_SIZES);
			mirror.defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();

			fFieldIDSize = mirror.readInt("field ID size", replyData); //$NON-NLS-1$
			fMethodIDSize = mirror.readInt("method ID size", replyData); //$NON-NLS-1$
			fObjectIDSize = mirror.readInt("object ID size", replyData); //$NON-NLS-1$
			fReferenceTypeIDSize = mirror.readInt("refType ID size", replyData); //$NON-NLS-1$
			fFrameIDSize = mirror.readInt("frame ID size", replyData); //$NON-NLS-1$
		} catch (IOException e) {
			fGotIDSizes = false;
			mirror.defaultIOExceptionHandler(e);
		} finally {
			mirror.handledJdwpRequest();
		}
	}

	/**
	 * Retrieves version info of the VM.
	 */
	public void getVersionInfo() {
		if (fVersionDescription != null) {
			return;
		}

		initJdwpRequest();
		try {
			JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.VM_VERSION);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();

			fVersionDescription = readString("version descr.", replyData); //$NON-NLS-1$
			fJdwpMajorVersion = readInt("major version", replyData); //$NON-NLS-1$
			fJdwpMinorVersion = readInt("minor version", replyData); //$NON-NLS-1$
			fVMVersion = readString("version", replyData); //$NON-NLS-1$
			fVMName = readString("name", replyData); //$NON-NLS-1$

			if ((fVMName != null) && fVMName.equals("KVM")) { //$NON-NLS-1$
				// KVM requires class preparation events in order
				// to resolve things correctly
				eventRequestManagerImpl().enableInternalClassPrepareEvent();
			}

		} catch (IOException e) {
			fVersionDescription = null;
			defaultIOExceptionHandler(e);
		} finally {
			handledJdwpRequest();
		}
	}

	/**
	 * Retrieves the HCR capabilities of the VM.
	 */
	public void getHCRCapabilities() {
		if (fHcrCapabilities != null) {
			return;
		}
		fHcrCapabilities = new boolean[HCR_CAN_REENTER_ON_EXIT + 1];

		if (isHCRSupported()) {
			initJdwpRequest();
			try {
				JdwpReplyPacket replyPacket = requestVM(JdwpCommandPacket.HCR_CAPABILITIES);
				defaultReplyErrorHandler(replyPacket.errorCode());
				DataInputStream replyData = replyPacket.dataInStream();

				fHcrCapabilities[HCR_CAN_RELOAD_CLASSES] = readBoolean(
						"reload classes", replyData); //$NON-NLS-1$
				fHcrCapabilities[HCR_CAN_GET_CLASS_VERSION] = readBoolean(
						"get class version", replyData); //$NON-NLS-1$
				fHcrCapabilities[HCR_CAN_DO_RETURN] = readBoolean(
						"do return", replyData); //$NON-NLS-1$
				fHcrCapabilities[HCR_CAN_REENTER_ON_EXIT] = readBoolean(
						"reenter on exit", replyData); //$NON-NLS-1$
			} catch (IOException e) {
				fHcrCapabilities = null;
				defaultIOExceptionHandler(e);
			} finally {
				handledJdwpRequest();
			}
		} else {
			for (int i = 0; i < fHcrCapabilities.length; i++) {
				fHcrCapabilities[i] = false;
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.hcr.VirtualMachine#canReloadClasses()
	 */
	@Override
	public boolean canReloadClasses() {
		getHCRCapabilities();
		return fHcrCapabilities[HCR_CAN_RELOAD_CLASSES];
	}

	/**
	 * @return Returns Whether VM can get the version of a given class file.
	 */
	public boolean canGetClassFileVersion1() {
		getHCRCapabilities();
		return fHcrCapabilities[HCR_CAN_GET_CLASS_VERSION];
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#canGetClassFileVersion()
	 */
	@Override
	public boolean canGetClassFileVersion() {
		return isJdwpVersionGreaterOrEqual(1, 6);
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#canGetConstantPool()
	 */
	@Override
	public boolean canGetConstantPool() {
		getCapabilities();
		return fCanGetConstantPool;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.hcr.VirtualMachine#canDoReturn()
	 */
	@Override
	public boolean canDoReturn() {
		getHCRCapabilities();
		return fHcrCapabilities[HCR_CAN_DO_RETURN];
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.hcr.VirtualMachine#canReenterOnExit()
	 */
	@Override
	public boolean canReenterOnExit() {
		getHCRCapabilities();
		return fHcrCapabilities[HCR_CAN_REENTER_ON_EXIT];
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.hcr.VirtualMachine#classesHaveChanged(java.lang.String[])
	 */
	@Override
	public int classesHaveChanged(String[] names) {
		checkHCRSupported();
		// We convert the class/interface names to signatures.
		String[] signatures = new String[names.length];

		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeInt(names.length, "length", outData); //$NON-NLS-1$
			for (int i = 0; i < names.length; i++) {
				signatures[i] = TypeImpl.classNameToSignature(names[i]);
				writeString(signatures[i], "signature", outData); //$NON-NLS-1$
			}

			JdwpReplyPacket replyPacket = requestVM(
					JdwpCommandPacket.HCR_CLASSES_HAVE_CHANGED, outBytes);
			defaultReplyErrorHandler(replyPacket.errorCode());
			DataInputStream replyData = replyPacket.dataInStream();

			byte resultFlag = readByte("result", resultHCRMap(), replyData); //$NON-NLS-1$
			switch (resultFlag) {
			case HCR_RELOAD_SUCCESS:
				return RELOAD_SUCCESS;
			case HCR_RELOAD_FAILURE:
				return RELOAD_FAILURE;
			case HCR_RELOAD_IGNORED:
				return RELOAD_IGNORED;
			}
			throw new InternalError(
					JDIMessages.VirtualMachineImpl_Invalid_result_flag_in_Classes_Have_Changed_response___3
							+ resultFlag + JDIMessages.VirtualMachineImpl__4); //
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return 0;
		} finally {
			handledJdwpRequest();
		}
	}

	/**
	 * @return Returns description of Mirror object.
	 */
	@Override
	public String toString() {
		try {
			return name();
		} catch (Exception e) {
			return fDescription;
		}
	}

	/**
	 * Retrieves constant mappings.
	 */
	public static void getConstantMaps() {
		if (fgHCRResultMap != null) {
			return;
		}

		Field[] fields = VirtualMachineImpl.class.getDeclaredFields();
		fgHCRResultMap = new HashMap<>();
		for (Field field : fields) {
			if ((field.getModifiers() & Modifier.PUBLIC) == 0
					|| (field.getModifiers() & Modifier.STATIC) == 0
					|| (field.getModifiers() & Modifier.FINAL) == 0) {
				continue;
			}

			try {
				String name = field.getName();
				if (name.startsWith("HCR_RELOAD_")) { //$NON-NLS-1$
					Integer intValue = Integer.valueOf(field.getInt(null));
					name = name.substring(4);
					fgHCRResultMap.put(intValue, name);
				}
			} 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 Map<Integer, String> resultHCRMap() {
		getConstantMaps();
		return fgHCRResultMap;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.VirtualMachine#setRequestTimeout(int)
	 */
	@Override
	public void setRequestTimeout(int timeout) {
		fRequestTimeout = timeout;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdi.VirtualMachine#getRequestTimeout()
	 */
	@Override
	public int getRequestTimeout() {
		return fRequestTimeout;
	}

	/**
	 * Returns whether the JDWP version is greater than or equal to the
	 * specified major/minor version numbers.
	 *
	 * @return whether the JDWP version is greater than or equal to the
	 *         specified major/minor version numbers
	 */
	public boolean isJdwpVersionGreaterOrEqual(int major, int minor) {
		getVersionInfo();
		return (fJdwpMajorVersion > major)
				|| (fJdwpMajorVersion == major && fJdwpMinorVersion >= minor);
	}

	@Override
	public void redefineClasses(Map<? extends ReferenceType, byte[]> typesToBytes) {
		if (!canRedefineClasses()) {
			throw new UnsupportedOperationException();
		}

		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeInt(typesToBytes.size(), "classes", outData); //$NON-NLS-1$

			Set<? extends ReferenceType> types = typesToBytes.keySet();
			Iterator<? extends ReferenceType> iter = types.iterator();
			while (iter.hasNext()) {
				ReferenceTypeImpl type = (ReferenceTypeImpl) iter.next();
				type.write(this, outData);
				byte[] bytes = typesToBytes.get(type);
				writeInt(bytes.length, "classfile", outData); //$NON-NLS-1$
				for (byte b : bytes) {
					writeByte(b, "classByte", outData); //$NON-NLS-1$
				}
				fCachedReftypes.remove(type.getRefTypeID()); // flush local
																// cache of
																// redefined
																// types
			}

			JdwpReplyPacket reply = requestVM(
					JdwpCommandPacket.VM_REDEFINE_CLASSES, outBytes);
			switch (reply.errorCode()) {
			case JdwpReplyPacket.UNSUPPORTED_VERSION:
				throw new UnsupportedClassVersionError();
			case JdwpReplyPacket.INVALID_CLASS_FORMAT:
				throw new ClassFormatError();
			case JdwpReplyPacket.CIRCULAR_CLASS_DEFINITION:
				throw new ClassCircularityError();
			case JdwpReplyPacket.FAILS_VERIFICATION:
				throw new VerifyError();
			case JdwpReplyPacket.NAMES_DONT_MATCH:
				throw new NoClassDefFoundError();
			case JdwpReplyPacket.ADD_METHOD_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Add_method_not_implemented_1);
			case JdwpReplyPacket.SCHEMA_CHANGE_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Scheme_change_not_implemented_2);
			case JdwpReplyPacket.HIERARCHY_CHANGE_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Hierarchy_change_not_implemented_3);
			case JdwpReplyPacket.DELETE_METHOD_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Delete_method_not_implemented_4);
			case JdwpReplyPacket.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Class_modifiers_change_not_implemented_5);
			case JdwpReplyPacket.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.VirtualMachineImpl_Method_modifiers_change_not_implemented_6);
			default:
				defaultReplyErrorHandler(reply.errorCode());
			}
		} catch (IOException ioe) {
			defaultIOExceptionHandler(ioe);
			return;
		} finally {
			handledJdwpRequest();
		}
	}

	/*
	 * @see VirtualMachine#canRedefineClasses()
	 */
	@Override
	public boolean canRedefineClasses() {
		getCapabilities();
		return fCanRedefineClasses;
	}

	/*
	 * @see VirtualMachine#canUseInstanceFilters()
	 */
	@Override
	public boolean canUseInstanceFilters() {
		getCapabilities();
		return fCanUseInstanceFilters;
	}

	/*
	 * @see VirtualMachine#canAddMethod()
	 */
	@Override
	public boolean canAddMethod() {
		getCapabilities();
		return fCanAddMethod;
	}

	/*
	 * @see VirtualMachine#canUnrestrictedlyRedefineClasses()
	 */
	@Override
	public boolean canUnrestrictedlyRedefineClasses() {
		getCapabilities();
		return fCanUnrestrictedlyRedefineClasses;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#canUseSourceNameFilters()
	 */
	@Override
	public boolean canUseSourceNameFilters() {
		getCapabilities();
		return fCanUseSourceNameFilters;
	}

	/*
	 * @see VirtualMachine#canPopFrames()
	 */
	@Override
	public boolean canPopFrames() {
		getCapabilities();
		return fCanPopFrames;
	}

	/*
	 * @see VirtualMachine#canGetSourceDebugExtension()
	 */
	@Override
	public boolean canGetSourceDebugExtension() {
		getCapabilities();
		return fCanGetSourceDebugExtension;
	}

	/*
	 * @see VirtualMachine#canRequestVMDeathEvent()
	 */
	@Override
	public boolean canRequestVMDeathEvent() {
		getCapabilities();
		return fCanRequestVMDeathEvent;
	}

	public boolean canSetDefaultStratum() {
		getCapabilities();
		return fCanSetDefaultStratum;
	}

	/*
	 * @see VirtualMachine#setDefaultStratum(String)
	 */
	@Override
	public void setDefaultStratum(String stratum) {
		fDefaultStratum = stratum;

		if (!canSetDefaultStratum()) {
			// TODO: how to inform the user that the VM doesn't manage
			// setDefaultStartum ?
			return;
		}
		if (stratum == null) {
			stratum = ""; //$NON-NLS-1$
		}
		initJdwpRequest();
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeString(stratum, "stratum ID", outData); //$NON-NLS-1$

			JdwpReplyPacket replyPacket = requestVM(
					JdwpCommandPacket.VM_SET_DEFAULT_STRATUM, outBytes);
			defaultReplyErrorHandler(replyPacket.errorCode());

		} catch (IOException e) {
			defaultIOExceptionHandler(e);
		} finally {
			handledJdwpRequest();
		}
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#getDefaultStratum()
	 */
	@Override
	public String getDefaultStratum() {
		return fDefaultStratum;
	}

	/* (non-Javadoc)
	 * @see com.sun.jdi.VirtualMachine#instanceCounts(java.util.List)
	 */
	@Override
	public long[] instanceCounts(List<? extends ReferenceType> refTypes) {
		if (refTypes == null) {
			throw new NullPointerException(JDIMessages.VirtualMachineImpl_2);
		}
		int size = refTypes.size();
		if (size == 0) {
			if (isJdwpVersionGreaterOrEqual(1, 6)) {
				return new long[0];
			}
			throw new UnsupportedOperationException(JDIMessages.ReferenceTypeImpl_27);
		}
		try {
			ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
			DataOutputStream outData = new DataOutputStream(outBytes);
			writeInt(size, "size", outData); //$NON-NLS-1$
			for (int i = 0; i < size; i++) {
				((ReferenceTypeImpl) refTypes.get(i)).getRefTypeID().write(
						outData);
			}
			JdwpReplyPacket replyPacket = requestVM(
					JdwpCommandPacket.VM_INSTANCE_COUNTS, outBytes);
			switch (replyPacket.errorCode()) {
			case JdwpReplyPacket.INVALID_CLASS:
			case JdwpReplyPacket.INVALID_OBJECT:
				throw new ObjectCollectedException(
						JDIMessages.class_or_object_not_known);
			case JdwpReplyPacket.ILLEGAL_ARGUMENT:
				throw new IllegalArgumentException(
						JDIMessages.VirtualMachineImpl_count_less_than_zero);
			case JdwpReplyPacket.NOT_IMPLEMENTED:
				throw new UnsupportedOperationException(
						JDIMessages.ReferenceTypeImpl_27);
			case JdwpReplyPacket.VM_DEAD:
				throw new VMDisconnectedException(JDIMessages.vm_dead);
			}
			defaultReplyErrorHandler(replyPacket.errorCode());

			DataInputStream replyData = replyPacket.dataInStream();
			int counts = readInt("counts", replyData); //$NON-NLS-1$
			if (counts != size) {
				throw new InternalError(JDIMessages.VirtualMachineImpl_3);
			}
			long[] ret = new long[counts];
			for (int i = 0; i < counts; i++) {
				ret[i] = readLong("ref count", replyData); //$NON-NLS-1$
			}
			return ret;
		} catch (IOException e) {
			defaultIOExceptionHandler(e);
			return null;
		} finally {
			handledJdwpRequest();
		}
	}

	/**
	 * Returns whether this VM is disconnected.
	 *
	 * @return whether this VM is disconnected
	 */
	public boolean isDisconnected() {
		return fIsDisconnected;
	}

	/**
	 * Sets whether this VM is disconnected.
	 *
	 * @param disconected
	 *            whether this VM is disconnected
	 */
	public synchronized void setDisconnected(boolean disconnected) {
		fIsDisconnected = disconnected;
	}

	/**
	 * Return the boolean type for this VM.
	 */
	protected BooleanTypeImpl getBooleanType() {
		if (fBooleanType == null) {
			fBooleanType = new BooleanTypeImpl(this);
		}
		return fBooleanType;
	}

	/**
	 * Return the byte type for this VM.
	 */
	protected ByteTypeImpl getByteType() {
		if (fByteType == null) {
			fByteType = new ByteTypeImpl(this);
		}
		return fByteType;
	}

	/**
	 * Return the char type for this VM.
	 */
	protected CharTypeImpl getCharType() {
		if (fCharType == null) {
			fCharType = new CharTypeImpl(this);
		}
		return fCharType;
	}

	/**
	 * Return the double type for this VM.
	 */
	protected DoubleTypeImpl getDoubleType() {
		if (fDoubleType == null) {
			fDoubleType = new DoubleTypeImpl(this);
		}
		return fDoubleType;
	}

	/**
	 * Return the float type for this VM.
	 */
	protected FloatTypeImpl getFloatType() {
		if (fFloatType == null) {
			fFloatType = new FloatTypeImpl(this);
		}
		return fFloatType;
	}

	/**
	 * Return the integer type for this VM.
	 */
	protected IntegerTypeImpl getIntegerType() {
		if (fIntegerType == null) {
			fIntegerType = new IntegerTypeImpl(this);
		}
		return fIntegerType;
	}

	/**
	 * Return the long type for this VM.
	 */
	protected LongTypeImpl getLongType() {
		if (fLongType == null) {
			fLongType = new LongTypeImpl(this);
		}
		return fLongType;
	}

	/**
	 * Return the short type for this VM.
	 */
	protected ShortTypeImpl getShortType() {
		if (fShortType == null) {
			fShortType = new ShortTypeImpl(this);
		}
		return fShortType;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see com.sun.jdi.VirtualMachine#canBeModified()
	 */
	@Override
	public boolean canBeModified() {
		return true;
	}

	public boolean mayCreateVirtualThreads() {
		return isJdwpVersionGreaterOrEqual(19, 0);
	}
}
