| /******************************************************************************* |
| * 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 |
| * 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.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jdi.internal.jdwp.JdwpClassID; |
| import org.eclipse.jdi.internal.jdwp.JdwpClassObjectID; |
| import org.eclipse.jdi.internal.jdwp.JdwpCommandPacket; |
| import org.eclipse.jdi.internal.jdwp.JdwpID; |
| import org.eclipse.jdi.internal.jdwp.JdwpReplyPacket; |
| |
| import com.sun.jdi.ClassNotLoadedException; |
| import com.sun.jdi.ClassNotPreparedException; |
| import com.sun.jdi.ClassType; |
| import com.sun.jdi.Field; |
| import com.sun.jdi.IncompatibleThreadStateException; |
| import com.sun.jdi.InvalidTypeException; |
| import com.sun.jdi.InvocationException; |
| import com.sun.jdi.Method; |
| import com.sun.jdi.ObjectReference; |
| import com.sun.jdi.ReferenceType; |
| import com.sun.jdi.ThreadReference; |
| 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 class ClassTypeImpl extends ReferenceTypeImpl implements ClassType { |
| |
| /** JDWP Tag. */ |
| public static final byte typeTag = JdwpID.TYPE_TAG_CLASS; |
| |
| /** The following are the stored results of JDWP calls. */ |
| private ClassTypeImpl fSuperclass = null; |
| |
| /** |
| * Creates new ClassTypeImpl. |
| */ |
| public ClassTypeImpl(VirtualMachineImpl vmImpl, JdwpClassID classID) { |
| super("ClassType", vmImpl, classID); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Creates new ClassTypeImpl. |
| */ |
| public ClassTypeImpl(VirtualMachineImpl vmImpl, JdwpClassID classID, |
| String signature, String genericSignature) { |
| super("ClassType", vmImpl, classID, signature, genericSignature); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @return Returns type tag. |
| */ |
| @Override |
| public byte typeTag() { |
| return typeTag; |
| } |
| |
| /** |
| * @return Create a null value instance of the type. |
| */ |
| @Override |
| public Value createNullValue() { |
| return new ClassObjectReferenceImpl(virtualMachineImpl(), |
| new JdwpClassObjectID(virtualMachineImpl())); |
| } |
| |
| /** |
| * Flushes all stored Jdwp results. |
| */ |
| @Override |
| public void flushStoredJdwpResults() { |
| super.flushStoredJdwpResults(); |
| |
| // For all classes that have this class cached as superclass, this cache |
| // must be undone. |
| Iterator<Object> itr = virtualMachineImpl().allCachedRefTypes(); |
| while (itr.hasNext()) { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl) itr.next(); |
| if (refType instanceof ClassTypeImpl) { |
| ClassTypeImpl classType = (ClassTypeImpl) refType; |
| if (classType.fSuperclass != null && classType.fSuperclass.equals(this)) { |
| classType.flushStoredJdwpResults(); |
| } |
| } |
| } |
| |
| fSuperclass = null; |
| } |
| |
| /** |
| * @return Returns a the single non-abstract Method visible from this class |
| * that has the given name and signature. |
| */ |
| @Override |
| public Method concreteMethodByName(String name, String signature) { |
| /* |
| * Recursion is used to find the method: The methods of its own (own |
| * methods() command); The methods of it's superclass. |
| */ |
| |
| Iterator<Method> methods = methods().iterator(); |
| Method method; |
| while (methods.hasNext()) { |
| method = methods.next(); |
| if (method.name().equals(name) && method.signature().equals(signature)) { |
| if (method.isAbstract()) { |
| return null; |
| } |
| return method; |
| } |
| } |
| |
| if (superclass() != null) { |
| return superclass().concreteMethodByName(name, signature); |
| } |
| |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ClassType#invokeMethod(com.sun.jdi.ThreadReference, com.sun.jdi.Method, java.util.List, int) |
| */ |
| @Override |
| public Value invokeMethod(ThreadReference thread, Method method, List<? extends Value> arguments, int options) throws InvalidTypeException, |
| ClassNotLoadedException, IncompatibleThreadStateException, |
| InvocationException { |
| return invokeMethod(thread, method, arguments, options, JdwpCommandPacket.CT_INVOKE_METHOD); |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ClassType#newInstance(com.sun.jdi.ThreadReference, com.sun.jdi.Method, java.util.List, int) |
| */ |
| @Override |
| public ObjectReference newInstance(ThreadReference thread, Method method, List<? extends Value> arguments, int options) throws InvalidTypeException, |
| ClassNotLoadedException, IncompatibleThreadStateException, |
| InvocationException { |
| checkVM(thread); |
| checkVM(method); |
| ThreadReferenceImpl threadImpl = (ThreadReferenceImpl) thread; |
| MethodImpl methodImpl = (MethodImpl) method; |
| |
| // Perform some checks for IllegalArgumentException. |
| if (!methods().contains(method)) |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Class_does_not_contain_given_method_4); |
| if (method.argumentTypeNames().size() != arguments.size()) |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Number_of_arguments_doesn__t_match_5); |
| if (!method.isConstructor()) |
| throw new IllegalArgumentException( |
| JDIMessages.ClassTypeImpl_Method_is_not_a_constructor_6); |
| |
| 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) { |
| checkVM(elt); |
| ((ValueImpl)elt).writeWithTag(this, outData); |
| } else { |
| ValueImpl.writeNullWithTag(this, outData); |
| } |
| } |
| |
| writeInt(optionsToJdwpOptions(options), |
| "options", MethodImpl.getInvokeOptions(), outData); //$NON-NLS-1$ |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.CT_NEW_INSTANCE, 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(); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| ObjectReferenceImpl object = ObjectReferenceImpl |
| .readObjectRefWithTag(this, replyData); |
| ObjectReferenceImpl exception = ObjectReferenceImpl |
| .readObjectRefWithTag(this, replyData); |
| if (exception != null) |
| throw new InvocationException(exception); |
| return object; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /** |
| * Assigns a value to a static field. . |
| */ |
| @Override |
| public void setValue(Field field, Value value) throws InvalidTypeException, |
| ClassNotLoadedException { |
| // Note that this information should not be cached. |
| initJdwpRequest(); |
| try { |
| ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); |
| DataOutputStream outData = new DataOutputStream(outBytes); |
| write(this, outData); |
| writeInt(1, "size", outData); // We only set one field //$NON-NLS-1$ |
| checkVM(field); |
| ((FieldImpl) field).write(this, outData); |
| |
| // check the type and the VM of the value. Convert the value if |
| // needed |
| ValueImpl checkedValue = ValueImpl.checkValue(value, field.type(), |
| virtualMachineImpl()); |
| |
| if (checkedValue != null) { |
| checkedValue.write(this, outData); |
| } else { |
| ValueImpl.writeNull(this, outData); |
| } |
| |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.CT_SET_VALUES, outBytes); |
| switch (replyPacket.errorCode()) { |
| case JdwpReplyPacket.TYPE_MISMATCH: |
| throw new InvalidTypeException(); |
| case JdwpReplyPacket.INVALID_CLASS: |
| throw new ClassNotLoadedException(name()); |
| } |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ClassType#subclasses() |
| */ |
| @Override |
| public List<ClassType> subclasses() { |
| // Note that this information should not be cached. |
| List<ClassType> subclasses = new ArrayList<>(); |
| Iterator<ReferenceType> itr = virtualMachineImpl().allRefTypes(); |
| while (itr.hasNext()) { |
| try { |
| ReferenceType refType = itr.next(); |
| if (refType instanceof ClassTypeImpl) { |
| ClassTypeImpl classType = (ClassTypeImpl) refType; |
| if (classType.superclass() != null |
| && classType.superclass().equals(this)) { |
| subclasses.add(classType); |
| } |
| } |
| } catch (ClassNotPreparedException e) { |
| continue; |
| } |
| } |
| return subclasses; |
| } |
| |
| /* (non-Javadoc) |
| * @see com.sun.jdi.ClassType#superclass() |
| */ |
| @Override |
| public ClassType superclass() { |
| if (fSuperclass != null) |
| return fSuperclass; |
| |
| initJdwpRequest(); |
| try { |
| JdwpReplyPacket replyPacket = requestVM( |
| JdwpCommandPacket.CT_SUPERCLASS, this); |
| defaultReplyErrorHandler(replyPacket.errorCode()); |
| DataInputStream replyData = replyPacket.dataInStream(); |
| fSuperclass = ClassTypeImpl.read(this, replyData); |
| return fSuperclass; |
| } catch (IOException e) { |
| defaultIOExceptionHandler(e); |
| return null; |
| } finally { |
| handledJdwpRequest(); |
| } |
| } |
| |
| /* |
| * @return Reads ID and returns known ReferenceTypeImpl with that ID, or if |
| * ID is unknown a newly created ReferenceTypeImpl. |
| */ |
| public static ClassTypeImpl read(MirrorImpl target, DataInputStream in) |
| throws IOException { |
| VirtualMachineImpl vmImpl = target.virtualMachineImpl(); |
| JdwpClassID ID = new JdwpClassID(vmImpl); |
| ID.read(in); |
| if (target.fVerboseWriter != null) |
| target.fVerboseWriter.println("classType", ID.value()); //$NON-NLS-1$ |
| |
| if (ID.isNull()) |
| return null; |
| |
| ClassTypeImpl mirror = (ClassTypeImpl) vmImpl.getCachedMirror(ID); |
| if (mirror == null) { |
| mirror = new ClassTypeImpl(vmImpl, ID); |
| vmImpl.addCachedMirror(mirror); |
| } |
| return mirror; |
| } |
| |
| /* |
| * @return Reads ID and returns known ReferenceTypeImpl with that ID, or if |
| * ID is unknown a newly created ReferenceTypeImpl. |
| */ |
| public static ClassTypeImpl readWithSignature(MirrorImpl target, |
| boolean withGenericSignature, DataInputStream in) |
| throws IOException { |
| VirtualMachineImpl vmImpl = target.virtualMachineImpl(); |
| JdwpClassID ID = new JdwpClassID(vmImpl); |
| ID.read(in); |
| if (target.fVerboseWriter != null) |
| target.fVerboseWriter.println("classType", ID.value()); //$NON-NLS-1$ |
| |
| String signature = target.readString("signature", in); //$NON-NLS-1$ |
| String genericSignature = null; |
| if (withGenericSignature) { |
| genericSignature = target.readString("generic signature", in); //$NON-NLS-1$ |
| } |
| if (ID.isNull()) |
| return null; |
| |
| ClassTypeImpl mirror = (ClassTypeImpl) vmImpl.getCachedMirror(ID); |
| if (mirror == null) { |
| mirror = new ClassTypeImpl(vmImpl, ID); |
| vmImpl.addCachedMirror(mirror); |
| } |
| mirror.setSignature(signature); |
| mirror.setGenericSignature(genericSignature); |
| return mirror; |
| } |
| |
| @Override |
| public boolean isEnum() { |
| if (virtualMachineImpl().isJdwpVersionGreaterOrEqual(1, 5)) { |
| // there is no modifier for this ... :( |
| ClassType superClass = superclass(); |
| return superClass != null |
| && "<E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/lang/Comparable<TE;>;Ljava/io/Serializable;".equals(superClass.genericSignature()); //$NON-NLS-1$ |
| } |
| // jdwp 1.5 only option |
| return false; |
| } |
| |
| } |