/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.kato.jvmti.javaruntime.model;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import javax.tools.diagnostics.image.CorruptData;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.image.DataUnavailable;
import javax.tools.diagnostics.image.ImagePointer;
import javax.tools.diagnostics.image.ImageSection;
import javax.tools.diagnostics.image.MemoryAccessException;
import javax.tools.diagnostics.runtime.java.JavaClass;
import javax.tools.diagnostics.runtime.java.JavaHeap;
import javax.tools.diagnostics.runtime.java.JavaObject;
import javax.tools.diagnostics.runtime.java.JavaReference;
import org.apache.kato.jvmti.process.CorruptDataImpl;
import org.apache.kato.jvmti.reader.CLogger;
/**
* Represents a Java object.
*
*/
public class JObject implements JavaObject {
private long objid = 0;
private long size = 0;
private Map<Long, Object> data = new HashMap<Long, Object>();
private JClass clazz = null;
private boolean visited = false;
private boolean isArray = false;
private Object[] objArray=null;
public Object[] getObjArray() {
return objArray;
}
public void setObjArray(Object[] objArray) {
this.objArray = objArray;
isArray=true;
}
public int objectSize = 0;
private JObject superClazzObject = null;
private Model model;
public JObject(JClass clazz, long objID, Model model) {
this.clazz = clazz;
this.objid = objID;
this.model = model;
if (clazz != null) {
try {
this.isArray = clazz.isArray();
} catch (CorruptDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
long getObjectID() {
return objid;
}
public void addValue(long fieldid, Object data) {
this.data.put(fieldid, data);
}
public double getDoubleValue(long id) {
Object obj = data.get(id);
if (obj instanceof Character) {
return ((Character) obj).charValue();
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve double from field");
}
Number d = (Number) obj; // First try from this object
if (d == null && superClazzObject != null) { // Then follow through the hierarchy
return superClazzObject.getDoubleValue(id);
}
return d.doubleValue();
}
public long getLongValue(long id) {
Object obj = data.get(id);
if (obj instanceof Character) {
return ((Character) obj).charValue();
} else if (obj instanceof Double) {
throw new IllegalArgumentException();
} else if (obj instanceof Float) {
throw new IllegalArgumentException();
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve long from field.");
}
Number d = (Number) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getLongValue(id);
}
return d.longValue();
}
public char getCharValue(long id) {
Object obj = data.get(id);
if (!(obj instanceof Character)) {
throw new IllegalArgumentException("Can only retrieve get character from a character.");
}
Character d = (Character) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getCharValue(id);
}
return d;
}
public boolean getBooleanValue(long id) {
Object obj = data.get(id);
if(!(obj instanceof Boolean)) {
throw new IllegalArgumentException("Cannot retrieve non-boolean values.");
}
Boolean d = (Boolean) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getBooleanValue(id);
}
return d;
}
public byte getByteValue(long id) {
Object obj = data.get(id);
if (obj instanceof Double) {
throw new IllegalArgumentException("Canot convert double to byte.");
} else if (obj instanceof Float) {
throw new IllegalArgumentException("Canot convert float to byte");
} else if (obj instanceof Long) {
throw new IllegalArgumentException("Canot convert double to byte.");
} else if (obj instanceof Integer) {
throw new IllegalArgumentException("Canot convert double to int.");
} else if (obj instanceof Short) {
throw new IllegalArgumentException("Canot convert short to int.");
} else if (obj instanceof Character) {
throw new IllegalArgumentException("Canot convert character to byte.");
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve byte from field");
}
Number d = (Number) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getByteValue(id);
}
return d.byteValue();
}
public float getFloatValue(long id) {
Object obj = data.get(id);
if (obj instanceof Double) {
throw new IllegalArgumentException("Cannot convert double to float.");
} else if (obj instanceof Character) {
return ((Character) obj).charValue();
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve float from field");
}
Number d = (Number) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getFloatValue(id);
}
return d.floatValue();
}
public int getIntValue(long id) {
Object obj = data.get(id);
if (obj instanceof Double) {
throw new IllegalArgumentException("Cannot convert double to int.");
} else if (obj instanceof Float) {
throw new IllegalArgumentException("Cannot convert float to int.");
} else if (obj instanceof Long) {
throw new IllegalArgumentException("Cannot convert long to int.");
} else if (obj instanceof Character) {
return ((Character) obj).charValue();
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve int from field");
}
Number d = (Number) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getIntValue(id);
}
return d.intValue();
}
public short getShortValue(long id) {
Object obj = data.get(id);
if (obj instanceof Double) {
throw new IllegalArgumentException("Cannot convert double to short.");
} else if (obj instanceof Float) {
throw new IllegalArgumentException("Cannot convert float to short.");
} else if (obj instanceof Long) {
throw new IllegalArgumentException("Canot convert long to short.");
} else if (obj instanceof Integer) {
throw new IllegalArgumentException("Canot convert int to short");
} else if (obj instanceof Character) {
throw new IllegalArgumentException("Canot convert char to short");
} else if(!(obj instanceof Number)) {
throw new IllegalArgumentException("Cannot retrieve short from field");
}
Number d = (Number) obj;
if (d == null && superClazzObject != null) {
return superClazzObject.getShortValue(id);
}
return d.shortValue();
}
public String getStringValue(long id) {
String d = (String) data.get(id);
if (d == null && superClazzObject != null) {
return superClazzObject.getStringValue(id);
}
return d;
}
public Object getValue(long id) {
if (data.containsKey(id)) {
Object jobj = data.get(id);
// Resolve a deferred reference.
if (jobj instanceof DeferredReference) {
return model.getObjectAtAddress(((DeferredReference) jobj).getPos());
}
return jobj;
} else {
if (superClazzObject != null) {
return superClazzObject.getValue(id);
} else {
System.out
.println("Missing field entry, this should not happen");
return null;
}
}
}
@Override
public void arraycopy(int srcStart, Object dst, int dstStart, int length)
throws CorruptDataException, MemoryAccessException {
if (!isArray) {
throw new IllegalArgumentException("Soruce object is not an array.");
}
if (dst == null) {
throw new NullPointerException("Destination array must not be null.");
}
if (srcStart < 0 || srcStart >= objArray.length) {
throw new IndexOutOfBoundsException("srcStart is out of bounds. 0 < "+
srcStart+" < "+objArray.length);
}
if (dstStart < 0 || dstStart >= Array.getLength(dst)) {
throw new IndexOutOfBoundsException("dstStart is out of bounds. 0 < "+
dstStart + " < " + Array.getLength(dst));
}
if(dstStart + length > Array.getLength(dst)) {
throw new IndexOutOfBoundsException("dstStart+length is out of bounds. "+
dstStart + length +" > " + Array.getLength(dst));
}
if(srcStart + length > objArray.length) {
if (srcStart < 0 || srcStart >= objArray.length) {
throw new IndexOutOfBoundsException("srcStart+length is out of bounds. "+
srcStart+length+" > "+objArray.length);
}
}
if (length < 0) {
throw new IndexOutOfBoundsException("length ("+length+") must be >=0.");
}
if (clazz.classSig.equals("[I")) {
CLogger.logr.log(Level.FINEST,"Array copy for int");
Integer[] ia = (Integer[]) objArray;
int[] outArray = (int[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = ia[srcStart+i];
}
} else if (clazz.classSig.equals("[F")) {
CLogger.logr.log(Level.FINEST,"Array copy for float");
Float[] fa = (Float[]) objArray;
float[] outArray = (float[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = fa[srcStart+i];
}
} else if (clazz.classSig.equals("[S")) {
CLogger.logr.log(Level.FINEST,"Array copy for short");
Short[] sa = (Short[]) objArray;
short[] outArray = (short[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = sa[srcStart+i];
}
} else if (clazz.classSig.equals("[J")) {
CLogger.logr.log(Level.FINEST,"Array copy for long");
Long[] ja = (Long[]) objArray;
long[] outArray = (long[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = ja[srcStart+i];
}
} else if (clazz.classSig.equals("[B")) {
CLogger.logr.log(Level.FINEST,"Array copy for byte");
Byte[] ba = (Byte[]) objArray;
byte[] outArray = (byte[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = ba[srcStart+i];
}
} else if (clazz.classSig.equals("[Z")) {
CLogger.logr.log(Level.FINEST,"Array copy for boolean");
Boolean[] za = (Boolean[]) objArray;
boolean[] outArray = (boolean[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = za[srcStart+i];
}
} else if (clazz.classSig.equals("[D")) {
CLogger.logr.log(Level.FINEST,"Array copy for double");
Double[] da = (Double[]) objArray;
double[] outArray = (double[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = da[srcStart+i];
}
} else if (clazz.classSig.equals("[C")) {
CLogger.logr.log(Level.FINEST,"Array copy for char");
Character[] ca = (Character[]) objArray;
char[] outArray = (char[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = ca[srcStart+i];
}
} else {
CLogger.logr.log(Level.FINEST,"...Object array");
JObject[] ca = (JObject[]) objArray;
JavaObject[] outArray = (JavaObject[]) dst;
for (int i = 0; i < length; i++) {
outArray[dstStart+i] = ca[srcStart+i];
}
}
}
@Override
public int getArraySize() throws CorruptDataException {
if(isArray==false) throw new IllegalArgumentException("object is not an array");
if(objArray==null) throw CorruptDataImpl.corruptDataException("underlying array object is null");
return objArray.length;
}
@Override
public long getHashcode() throws DataUnavailable, CorruptDataException {
throw new DataUnavailable("Hashcode not available.");
}
@Override
public JavaHeap getHeap() throws CorruptDataException, DataUnavailable {
return model.getHeap();
}
@Override
public ImagePointer getID() {
return new SimpleImagePointer(objid);
}
@Override
public JavaClass getJavaClass() throws CorruptDataException {
CLogger.logr.log(Level.FINEST,"asking for class " + clazz);
return clazz;
}
@Override
public long getPersistentHashcode() throws DataUnavailable,
CorruptDataException {
throw new DataUnavailable("Persistent Hashcode not available in CJVMTI.");
}
@Override
public List<JavaReference> getReferences() {
return Collections.emptyList();
}
@Override
public List<ImageSection> getSections() {
return Collections.emptyList();
}
@Override
public long getSize() throws CorruptDataException {
return size;
}
@Override
public boolean isArray() throws CorruptDataException {
return isArray;
}
public String toString() {
if (clazz == null) {
return "<unknown>@" + objid;
}
try {
return clazz.getName() + "@" + objid;
} catch (CorruptDataException e) {
return super.toString();
}
}
public void setSuperClazzObj(JObject supClazz) {
this.superClazzObject = supClazz;
}
public int getNumFields(){
return data.size();
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public boolean isVisited() {
return visited;
}
public boolean equals(Object obj) {
if (obj instanceof JObject) {
return getObjectID() == ((JObject) obj).getObjectID();
}
return false;
}
/**
* Checks if an instance of this object would
* be compatible with the passed class.
*
*
* @param type Class to check compatiblity with.
* @return true if instance of passed clazz
*/
public boolean isInstance(JClass type) {
if (clazz.equals(type)) {
return true;
} else if (superClazzObject != null) {
return superClazzObject.isInstance(type);
} else {
return false;
}
}
}