ArrayReferenceImpl

Created Diff never expires
21 删除
总计
删除
单词
总计
删除
要继续使用此功能,请升级到
Diffchecker logo
Diffchecker Pro
284
88 添加
总计
添加
单词
总计
添加
要继续使用此功能,请升级到
Diffchecker logo
Diffchecker Pro
351
/*
/*
* Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
*
* This code is free software; you can redistribute it and/or modify it
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
* by Oracle in the LICENSE file that accompanied this code.
*
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
* accompanied this code).
*
*
* You should have received a copy of the GNU General Public License version
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* or visit www.oracle.com if you need additional information or have any
* questions.
* questions.
*
* Copyright (C) 2019 JetBrains s.r.o.
*
* This program is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public License v2 with Classpath Exception.
* The text of the license is available in the file LICENSE.TXT.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.TXT for more details.
*
* You may contact JetBrains s.r.o. at Na Hřebenech II 1718/10, 140 00 Prague,
* Czech Republic or at legal@jetbrains.com.
*/
*/


package com.sun.tools.jdi;
package com.jetbrains.jdi;

import com.sun.jdi.*;


import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.Arrays;

import java.util.Iterator;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.Method;
import com.sun.jdi.Type;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;


public class ArrayReferenceImpl extends ObjectReferenceImpl
public class ArrayReferenceImpl extends ObjectReferenceImpl
implements ArrayReference
implements ArrayReference
{
{
int length = -1;
private volatile int length = -1;


ArrayReferenceImpl(VirtualMachine aVm,long aRef) {
ArrayReferenceImpl(VirtualMachine aVm, long aRef) {
super(aVm,aRef);
super(aVm, aRef);
}
}


protected ClassTypeImpl invokableReferenceType(Method method) {
protected ClassTypeImpl invokableReferenceType(Method method) {
// The method has to be a method on Object since
// The method has to be a method on Object since
// arrays don't have methods nor any other 'superclasses'
// arrays don't have methods nor any other 'superclasses'
// So, use the ClassTypeImpl for Object instead of
// So, use the ClassTypeImpl for Object instead of
// the ArrayTypeImpl for the array itself.
// the ArrayTypeImpl for the array itself.
return (ClassTypeImpl)method.declaringType();
return (ClassTypeImpl)method.declaringType();
}
}


ArrayTypeImpl arrayType() {
ArrayTypeImpl arrayType() {
return (ArrayTypeImpl)type();
return (ArrayTypeImpl)type();
}
}


/**
/**
* Return array length.
* Return array length.
* Need not be synchronized since it cannot be provably stale.
* Need not be synchronized since it cannot be provably stale.
*/
*/
public int length() {
public int length() {
if(length == -1) {
if(length == -1) {
try {
try {
length = JDWP.ArrayReference.Length.
length = JDWP.ArrayReference.Length.
process(vm, this).arrayLength;
process(vm, this).arrayLength;
} catch (JDWPException exc) {
} catch (JDWPException exc) {
throw exc.toJDIException();
throw exc.toJDIException();
}
}
}
}
return length;
return length;
}
}


public CompletableFuture<Integer> lengthAsync() {
if (length != -1) {
return CompletableFuture.completedFuture(length);
}
return JDWP.ArrayReference.Length.processAsync(vm, this).thenApply(r -> length = r.arrayLength);
}

void setLength(int length) {
this.length = length;
}

public Value getValue(int index) {
public Value getValue(int index) {
List<Value> list = getValues(index, 1);
return getValues(index, 1).get(0);
return list.get(0);
}

public CompletableFuture<Value> getValueAsync(int index) {
return getValuesAsync(index, 1).thenApply(r -> r.get(0));
}
}


public List<Value> getValues() {
public List<Value> getValues() {
return getValues(0, -1);
return getValues(0, -1);
}
}


public CompletableFuture<List<Value>> getValuesAsync() {
return getValuesAsync(0, -1);
}

/**
/**
* Validate that the range to set/get is valid.
* Validate that the range to set/get is valid.
* length of -1 (meaning rest of array) has been converted
* length of -1 (meaning rest of array) has been converted
* before entry.
* before entry.
*/
*/
private void validateArrayAccess(int index, int length) {
private void validateArrayAccess(int index, int length) {
// because length can be computed from index,
// because length can be computed from index,
// index must be tested first for correct error message
// index must be tested first for correct error message
if ((index < 0) || (index > length())) {
if ((index < 0) || (index > length())) {
throw new IndexOutOfBoundsException(
throw new IndexOutOfBoundsException(
"Invalid array index: " + index);
"Invalid array index: " + index);
}
}
if (length < 0) {
if (length < 0) {
throw new IndexOutOfBoundsException(
throw new IndexOutOfBoundsException(
"Invalid array range length: " + length);
"Invalid array range length: " + length);
}
}
if (index + length > length()) {
if (index + length > length()) {
throw new IndexOutOfBoundsException(
throw new IndexOutOfBoundsException(
"Invalid array range: " +
"Invalid array range: " +
index + " to " + (index + length - 1));
index + " to " + (index + length - 1));
}
}
}
}


@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
private static <T> T cast(Object x) {
private static <T> T cast(Object x) {
return (T)x;
return (T)x;
}
}


public CompletableFuture<List<Value>> getValuesAsync(int index, int len) {
return lengthAsync().thenCompose(__ -> { // preload length
int length = len;
if (length == -1) { // -1 means the rest of the array
length = length() - index;
}
validateArrayAccess(index, length);
if (length == 0) {
return CompletableFuture.completedFuture(Collections.emptyList());
}

return JDWP.ArrayReference.GetValues.processAsync(vm, this, index, length)
.thenApply(r -> cast(r.values));
});
}

public List<Value> getValues(int index, int length) {
public List<Value> getValues(int index, int length) {
if (length == -1) { // -1 means the rest of the array
if (length == -1) { // -1 means the rest of the array
length = length() - index;
length = length() - index;
}
}
validateArrayAccess(index, length);
validateArrayAccess(index, length);
if (length == 0) {
if (length == 0) {
return new ArrayList<Value>();
return Collections.emptyList();
}
}


List<Value> vals;
List<Value> vals;
try {
try {
vals = cast(JDWP.ArrayReference.GetValues.process(vm, this, index, length).values);
vals = cast(JDWP.ArrayReference.GetValues.process(vm, this, index, length).values);
} catch (JDWPException exc) {
} catch (JDWPException exc) {
throw exc.toJDIException();
throw exc.toJDIException();
}
}


return vals;
return vals;
}
}


public void setValue(int index, Value value)
public void setValue(int index, Value value)
throws InvalidTypeException,
throws InvalidTypeException,
ClassNotLoadedException {
ClassNotLoadedException {
List<Value> list = new ArrayList<Value>(1);
setValues(index, Collections.singletonList(value), 0, 1);
list.add(value);
setValues(index, list, 0, 1);
}
}


public void setValues(List<? extends Value> values)
public void setValues(List<? extends Value> values)
throws InvalidTypeException,
throws InvalidTypeException,
ClassNotLoadedException {
ClassNotLoadedException {
setValues(0, values, 0, -1);
setValues(0, values, 0, -1);
}
}


public void setValues(int index, List<? extends Value> values,
public void setValues(int index, List<? extends Value> values,
int srcIndex, int length)
int srcIndex, int length)
throws InvalidTypeException,
throws InvalidTypeException,
ClassNotLoadedException {
setValues(index, values, srcIndex, length, true);
}

public void setValues(int index, List<? extends Value> values,
int srcIndex, int length, boolean checkAssignable)
throws InvalidTypeException,
ClassNotLoadedException {
ClassNotLoadedException {


if (length == -1) { // -1 means the rest of the array
if (length == -1) { // -1 means the rest of the array
// shorter of, the rest of the array and rest of
// shorter of, the rest of the array and rest of
// the source values
// the source values
length = Math.min(length() - index,
length = Math.min(length() - index,
values.size() - srcIndex);
values.size() - srcIndex);
}
}
validateMirrorsOrNulls(values);
validateMirrorsOrNulls(values);
validateArrayAccess(index, length);
validateArrayAccess(index, length);


if ((srcIndex < 0) || (srcIndex > values.size())) {
if ((srcIndex < 0) || (srcIndex > values.size())) {
throw new IndexOutOfBoundsException(
throw new IndexOutOfBoundsException(
"Invalid source index: " + srcIndex);
"Invalid source index: " + srcIndex);
}
}
if (srcIndex + length > values.size()) {
if (srcIndex + length > values.size()) {
throw new IndexOutOfBoundsException(
throw new IndexOutOfBoundsException(
"Invalid source range: " +
"Invalid source range: " +
srcIndex + " to " +
srcIndex + " to " +
(srcIndex + length - 1));
(srcIndex + length - 1));
}
}


boolean somethingToSet = false;;
boolean somethingToSet = false;
ValueImpl[] setValues = new ValueImpl[length];
ValueImpl[] setValues = new ValueImpl[length];


for (int i = 0; i < length; i++) {
for (int i = 0; i < length; i++) {
ValueImpl value = (ValueImpl)values.get(srcIndex + i);
ValueImpl value = (ValueImpl)values.get(srcIndex + i);


try {
try {
// Validate and convert if necessary
// Validate and convert if necessary
setValues[i] =
setValues[i] = prepareForAssignment(value, new Component(checkAssignable));
ValueImpl.prepareForAssignment(value,
new Component());
somethingToSet = true;
somethingToSet = true;
} catch (ClassNotLoadedException e) {
} catch (ClassNotLoadedException e) {
/*
/*
* Since we got this exception,
* Since we got this exception,
* the component must be a reference type.
* the component must be a reference type.
* This means the class has not yet been loaded
* This means the class has not yet been loaded
* through the defining class's class loader.
* through the defining class's class loader.
* If the value we're trying to set is null,
* If the value we're trying to set is null,
* then setting to null is essentially a
* then setting to null is essentially a
* no-op, and we should allow it without an
* no-op, and we should allow it without an
* exception.
* exception.
*/
*/
if (value != null) {
if (value != null) {
throw e;
throw e;
}
}
}
}
}
}
if (somethingToSet) {
if (somethingToSet) {
try {
try {
JDWP.ArrayReference.SetValues.
JDWP.ArrayReference.SetValues.
process(vm, this, index, setValues);
process(vm, this, index, setValues);
} catch (JDWPException exc) {
} catch (JDWPException exc) {
throw exc.toJDIException();
throw exc.toJDIException();
}
}
}
}
}
}


public String toString() {
public String toString() {
return "instance of " + arrayType().componentTypeName() +
return "instance of " + arrayType().componentTypeName() +
"[" + length() + "] (id=" + uniqueID() + ")";
"[" + length() + "] (id=" + uniqueID() + ")";
}
}


byte typeValueKey() {
byte typeValueKey() {
return JDWP.Tag.ARRAY;
return JDWP.Tag.ARRAY;
}
}


void validateAssignment(ValueContainer destination)
void validateAssignment(ValueContainer destination)
throws InvalidTypeException, ClassNotLoadedException {
throws InvalidTypeException, ClassNotLoadedException {
try {
try {
super.validateAssignment(destination);
super.validateAssignment(destination);
} catch (ClassNotLoadedException e) {
} catch (ClassNotLoadedException e) {
/*
/*
* An array can be used extensively without the
* An array can be used extensively without the
* enclosing loader being recorded by the VM as an
* enclosing loader being recorded by the VM as an
* initiating loader of the array type. In addition, the
* initiating loader of the array type. In addition, the
* load of an array class is fairly harmless as long as
* load of an array class is fairly harmless as long as
* the component class is already loaded. So we relax the
* the component class is already loaded. So we relax the
* rules a bit and allow the assignment as long as the
* rules a bit and allow the assignment as long as the
* ultimate component types are assignable.
* ultimate component types are assignable.
*/
*/
boolean valid = false;
boolean valid = false;
JNITypeParser destParser = new JNITypeParser(
JNITypeParser destParser = new JNITypeParser(
destination.signature());
destination.signature());
JNITypeParser srcParser = new JNITypeParser(
JNITypeParser srcParser = new JNITypeParser(
arrayType().signature());
arrayType().signature());
int destDims = destParser.dimensionCount();
int destDims = destParser.dimensionCount();
if (destDims <= srcParser.dimensionCount()) {
if (destDims <= srcParser.dimensionCount()) {
/*
/*
* Remove all dimensions from the destination. Remove
* Remove all dimensions from the destination. Remove
* the same number of dimensions from the source.
* the same number of dimensions from the source.
* Get types for both and check to see if they are
* Get types for both and check to see if they are
* compatible.
* compatible.
*/
*/
String destComponentSignature =
String destComponentSignature =
destParser.componentSignature(destDims);
destParser.componentSignature(destDims);
Type destComponentType =
Type destComponentType =
destination.findType(destComponentSignature);
destination.findType(destComponentSignature);
String srcComponentSignature =
String srcComponentSignature =
srcParser.componentSignature(destDims);
srcParser.componentSignature(destDims);
Type srcComponentType =
Type srcComponentType =
arrayType().findComponentType(srcComponentSignature);
arrayType().findComponentType(srcComponentSignature);
valid = ArrayTypeImpl.isComponentAssignable(destComponentType,
valid = ArrayTypeImpl.isComponentAssignable(destComponentType,
srcComponentType);
srcComponentType);
}
}


if (!valid) {
if (!valid) {
throw new InvalidTypeException("Cannot assign " +
throw new InvalidTypeException("Cannot assign " +
arrayType().name() +
arrayType().name() +
" to " +
" to " +
destination.typeName());
destination.typeName());
}
}
}
}
}
}


/*
/*
* Represents an array component to other internal parts of this
* Represents an array component to other internal parts of this
* implementation. This is not exposed at the JDI level. Currently,
* implementation. This is not exposed at the JDI level. Currently,
* this class is needed only for type checking so it does not even
* this class is needed only for type checking so it does not even
* reference a particular component - just a generic component
* reference a particular component - just a generic component
* of this array. In the future we may need to expand its use.
* of this array. In the future we may need to expand its use.
*/
*/
class Component implements ValueContainer {
class Component implements ValueContainer {
private final boolean checkAssignable;

public Component(boolean checkAssignable) {
super();
this.checkAssignable = checkAssignable;
}

public Type type() throws ClassNotLoadedException {
public Type type() throws ClassNotLoadedException {
return arrayType().componentType();
return arrayType().componentType();
}
}
public String typeName() {
public String typeName() {
return arrayType().componentTypeName();
return arrayType().componentTypeName();
}
}
public String signature() {
public String signature() {
return arrayType().componentSignature();
return arrayType().componentSignature();
}
}
public Type findType(String signature) throws ClassNotLoadedException {
public Type findType(String signature) throws ClassNotLoadedException {
return arrayType().findComponentType(signature);
return arrayType().findComponentType(signature);
}
}

@Override
public boolean checkAssignable() {
return checkAssignable;
}
}
}
}
}