| /******************************************************************************* |
| * 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.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.JdwpID; |
| import org.eclipse.jdi.internal.jdwp.JdwpObjectID; |
| |
| import com.ibm.icu.text.MessageFormat; |
| import com.sun.jdi.ArrayType; |
| import com.sun.jdi.ClassNotLoadedException; |
| import com.sun.jdi.ClassType; |
| import com.sun.jdi.InterfaceType; |
| import com.sun.jdi.InternalException; |
| import com.sun.jdi.InvalidTypeException; |
| import com.sun.jdi.PrimitiveType; |
| import com.sun.jdi.ReferenceType; |
| import com.sun.jdi.Type; |
| import com.sun.jdi.Value; |
| import com.sun.jdi.VoidType; |
| |
| /** |
| * this class implements the corresponding interfaces declared by the JDI |
| * specification. See the com.sun.jdi package for more information. |
| * |
| */ |
| public abstract class ValueImpl extends MirrorImpl implements Value { |
| /** |
| * Creates new ValueImpl. |
| */ |
| protected ValueImpl(String description, VirtualMachineImpl vmImpl) { |
| super(description, vmImpl); |
| } |
| |
| /** |
| * @returns type of value. |
| */ |
| @Override |
| public abstract Type type(); |
| |
| /** |
| * @returns type of value. |
| */ |
| public abstract byte getTag(); |
| |
| /** |
| * @return Reads JDWP representation and returns new instance. |
| */ |
| public static ValueImpl readWithTag(MirrorImpl target, DataInputStream in) |
| throws IOException { |
| byte tag = target.readByte("object tag", JdwpID.tagMap(), in); //$NON-NLS-1$ |
| return readWithoutTag(target, tag, in); |
| } |
| |
| /** |
| * @return Reads JDWP representation and returns new instance. |
| */ |
| public static ValueImpl readWithoutTag(MirrorImpl target, int type, |
| DataInputStream in) throws IOException { |
| VirtualMachineImpl vmImpl = target.virtualMachineImpl(); |
| // See also ArrayReference Impl. |
| switch (type) { |
| case ArrayReferenceImpl.tag: |
| return ArrayReferenceImpl.read(target, in); |
| case ClassLoaderReferenceImpl.tag: |
| return ClassLoaderReferenceImpl.read(target, in); |
| case ClassObjectReferenceImpl.tag: |
| return ClassObjectReferenceImpl.read(target, in); |
| case StringReferenceImpl.tag: |
| return StringReferenceImpl.read(target, in); |
| case ObjectReferenceImpl.tag: |
| return ObjectReferenceImpl.readObjectRefWithoutTag(target, in); |
| case ThreadGroupReferenceImpl.tag: |
| return ThreadGroupReferenceImpl.read(target, in); |
| case ThreadReferenceImpl.tag: |
| return ThreadReferenceImpl.read(target, in); |
| case BooleanValueImpl.tag: |
| return BooleanValueImpl.read(target, in); |
| case ByteValueImpl.tag: |
| return ByteValueImpl.read(target, in); |
| case CharValueImpl.tag: |
| return CharValueImpl.read(target, in); |
| case DoubleValueImpl.tag: |
| return DoubleValueImpl.read(target, in); |
| case FloatValueImpl.tag: |
| return FloatValueImpl.read(target, in); |
| case IntegerValueImpl.tag: |
| return IntegerValueImpl.read(target, in); |
| case LongValueImpl.tag: |
| return LongValueImpl.read(target, in); |
| case ShortValueImpl.tag: |
| return ShortValueImpl.read(target, in); |
| case VoidValueImpl.tag: |
| return new VoidValueImpl(vmImpl); |
| case 0: |
| return null; |
| default: |
| throw new InternalException( |
| JDIMessages.ValueImpl_Invalid_Value_tag_encountered___1 |
| + type); |
| } |
| } |
| |
| /** |
| * Writes value with value tag. |
| */ |
| public void writeWithTag(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| target.writeByte(getTag(), "tag", JdwpID.tagMap(), out); //$NON-NLS-1$ |
| write(target, out); |
| } |
| |
| /** |
| * Writes value without value tag. |
| */ |
| public abstract void write(MirrorImpl target, DataOutputStream out) |
| throws IOException; |
| |
| /** |
| * Writes null value without value tag. |
| */ |
| public static void writeNull(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| JdwpObjectID nullID = new JdwpObjectID(target.virtualMachineImpl()); |
| nullID.write(out); |
| if (target.fVerboseWriter != null) |
| target.fVerboseWriter.println("objectReference", nullID.value()); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Writes null value with value tag. |
| */ |
| public static void writeNullWithTag(MirrorImpl target, DataOutputStream out) |
| throws IOException { |
| target.writeByte(ObjectReferenceImpl.tag, "tag", JdwpID.tagMap(), out); //$NON-NLS-1$ |
| writeNull(target, out); |
| } |
| |
| /** |
| * Check the type and the vm of each values, according to the associated |
| * type. For primitive values, convert the value for match the given type if |
| * needed. The two list must have the same size. |
| * |
| * @return the (converted) values. |
| * @see checkValue(Value, Type, VirtualMachineImpl) |
| */ |
| protected static List<Value> checkValues(List<?extends Value> values, List<Type> types, |
| VirtualMachineImpl vm) throws InvalidTypeException { |
| List<Value> result = new ArrayList<>(values.size()); |
| Iterator<? extends Value> iterValues = values.iterator(); |
| Iterator<Type> iterTypes = types.iterator(); |
| while (iterValues.hasNext()) { |
| Value value = iterValues.next(); |
| Type type = iterTypes.next(); |
| result.add(checkValue(value, type, vm)); |
| } |
| return result; |
| } |
| |
| /** |
| * Check the type and the vm of the given value. In case of primitive value, |
| * the value is converted if needed. |
| * |
| * @return the (converted) value. |
| * @throws InvalidTypeException |
| * if the given value is no assignment compatible with the given |
| * type. |
| * @see checkPrimitiveValue(PrimitiveValueImpl, PrimitiveTypeImpl, |
| * PrimitiveTypeImpl) |
| */ |
| public static ValueImpl checkValue(Value value, Type type, |
| VirtualMachineImpl vm) throws InvalidTypeException { |
| if (value == null) { |
| if (!(type instanceof PrimitiveType)) { |
| return null; |
| } |
| } else { |
| vm.checkVM(value); |
| TypeImpl valueType = (TypeImpl) value.type(); |
| if (valueType instanceof PrimitiveType |
| && type instanceof PrimitiveType) { |
| return checkPrimitiveValue((PrimitiveValueImpl) value, |
| (PrimitiveTypeImpl) valueType, (PrimitiveTypeImpl) type); |
| } |
| if (valueType instanceof ReferenceType |
| && type instanceof ReferenceType) { |
| checkReferenceType((ReferenceType) valueType, |
| (ReferenceType) type); |
| return (ValueImpl) value; |
| } |
| if (valueType instanceof VoidType && type instanceof VoidType) { |
| return (VoidValueImpl) value; |
| } |
| } |
| throw new InvalidTypeException( |
| MessageFormat |
| .format(JDIMessages.ValueImpl_Type_of_the_value_not_compatible_with_the_expected_type__1, |
| new Object[] { |
| value != null ? value.type().name() |
| : "null", type.name() })); //$NON-NLS-1$ |
| } |
| |
| /** |
| */ |
| private static void checkReferenceType(ReferenceType valueType, |
| ReferenceType type) throws InvalidTypeException { |
| if (valueType instanceof ArrayType) { |
| if (type instanceof ArrayType) { |
| try { |
| Type valueComponentType = ((ArrayType) valueType) |
| .componentType(); |
| Type componentType = ((ArrayType) type).componentType(); |
| if (valueComponentType instanceof PrimitiveType) { |
| if (valueComponentType.equals(componentType)) { |
| return; |
| } |
| } else if (valueComponentType instanceof ReferenceType |
| && componentType instanceof ReferenceType) { |
| checkReferenceType((ReferenceType) valueComponentType, |
| (ReferenceType) componentType); |
| return; |
| } |
| } catch (ClassNotLoadedException e) { |
| // should not append |
| } |
| } else { |
| // an array can be assigned to an object |
| if (type.signature().equals("Ljava/lang/Object;")) { //$NON-NLS-1$ |
| return; |
| } |
| } |
| } else { |
| if (type instanceof ClassType) { |
| if (valueType instanceof ClassType) { |
| ClassType superClass = (ClassType) valueType; |
| while (superClass != null) { |
| if (superClass.equals(type)) { |
| return; |
| } |
| superClass = superClass.superclass(); |
| } |
| } else if (valueType instanceof InterfaceType) { |
| // an interface can be assigned to an object |
| if (type.signature().equals("Ljava/lang/Object;")) { //$NON-NLS-1$ |
| return; |
| } |
| } |
| } else if (type instanceof InterfaceType) { |
| if (valueType instanceof InterfaceType) { |
| if (checkInterfaceType((InterfaceType) valueType, |
| (InterfaceType) type)) { |
| return; |
| } |
| } else { |
| List<InterfaceType> interfaces = ((ClassType) valueType).allInterfaces(); |
| for (Iterator<InterfaceType> iter = interfaces.iterator(); iter.hasNext();) { |
| if (checkInterfaceType(iter.next(), |
| (InterfaceType) type)) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| throw new InvalidTypeException( |
| MessageFormat |
| .format(JDIMessages.ValueImpl_Type_of_the_value_not_compatible_with_the_expected_type__1, |
| new Object[] { valueType.name(), type.name() })); |
| } |
| |
| private static boolean checkInterfaceType(InterfaceType valueType, |
| InterfaceType type) { |
| if (valueType.equals(type)) { |
| return true; |
| } |
| List<InterfaceType> superInterfaces = valueType.superinterfaces(); |
| for (Iterator<InterfaceType> iter = superInterfaces.iterator(); iter.hasNext();) { |
| if (checkInterfaceType(iter.next(), type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Check the type of the given value, and convert the value to the given |
| * type if needed (see Java Language Spec, section 5.2). |
| * |
| * @return the (converted) value. |
| * @throws InvalidTypeException |
| * if the given value is no assignment compatible with the given |
| * type. |
| */ |
| protected static ValueImpl checkPrimitiveValue(PrimitiveValueImpl value, |
| PrimitiveTypeImpl valueType, PrimitiveTypeImpl type) |
| throws InvalidTypeException { |
| char valueTypeSignature = valueType.signature().charAt(0); |
| char typeSignature = type.signature().charAt(0); |
| if (valueTypeSignature == typeSignature) { |
| return value; |
| } |
| VirtualMachineImpl vm = value.virtualMachineImpl(); |
| switch (typeSignature) { |
| case 'D': |
| if (valueTypeSignature != 'Z') { |
| return new DoubleValueImpl(vm, new Double(value.doubleValue())); |
| } |
| break; |
| case 'F': |
| if (valueTypeSignature != 'Z' && valueTypeSignature != 'D') { |
| return new FloatValueImpl(vm, new Float(value.floatValue())); |
| } |
| break; |
| case 'J': |
| if (valueTypeSignature != 'Z' && valueTypeSignature != 'D' |
| && valueTypeSignature != 'F') { |
| return new LongValueImpl(vm, new Long(value.longValue())); |
| } |
| break; |
| case 'I': |
| if (valueTypeSignature == 'B' || valueTypeSignature == 'C' |
| || valueTypeSignature == 'S') { |
| return new IntegerValueImpl(vm, new Integer(value.intValue())); |
| } |
| break; |
| case 'S': |
| if (valueTypeSignature == 'B') { |
| return new ShortValueImpl(vm, new Short(value.shortValue())); |
| } |
| break; |
| } |
| throw new InvalidTypeException( |
| MessageFormat |
| .format(JDIMessages.ValueImpl_Type_of_the_value_not_compatible_with_the_expected_type__1, |
| new Object[] { valueType.name(), type.name() })); |
| } |
| } |