package com.google.code.vimsztool.debug;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.google.code.vimsztool.exception.ExpressionEvalException;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ArrayType;
import com.sun.jdi.BooleanType;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.ClassType;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.InterfaceType;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.PrimitiveType;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Type;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
public class ExpressionEval {
private static String[] primitiveTypeNames = { "boolean", "byte", "char",
"short", "int", "long", "float", "double" };
private static int getMaxLength(List<String> names) {
int max = 0;
if (names == null || names.size() ==0) return 0;
for (String name : names ) {
int len = name.length();
if (len > max) max = len;
}
return max;
}
public static String eval(List<Expression> exps) {
if (exps ==null || exps.size() == 0) return "";
StringBuilder sb = new StringBuilder();
int count = 0;
List<String> oriExps = new ArrayList<String>();
for (Expression exp : exps ) {
oriExps.add(exp.getOriExp());
}
int maxLen = getMaxLength(oriExps)+2;
for (Expression exp :exps) {
count ++;
sb.append(padStr(maxLen,exp.getOriExp())).append(":");
try {
Value value = getJdiValue(exp);
sb.append(getPrettyPrintStr(value)).append("\n");
} catch (ExpressionEvalException e) {
sb.append("exception in calc the value");
}
}
return sb.toString();
}
public static String executeEvalCmd(String debugCmd,String debugCmdArgs) {
String actionResult = null;
try {
if (debugCmd.equals("eval") || debugCmd.equals("print")) {
String exp = debugCmdArgs.substring(debugCmd.length()+1);
actionResult = eval(exp);
} else if (debugCmd.equals("inspect")) {
String exp = debugCmdArgs.substring(debugCmd.length()+1);
actionResult = inspect(exp);
} else if (debugCmd.equals("locals")) {
actionResult = variables();
} else if (debugCmd.equals("fields")) {
actionResult = fields();
} else if (debugCmd.equals("reftype")) {
String exp = debugCmdArgs.substring(debugCmd.length()+1);
actionResult = reftype(exp);
}
return actionResult;
} catch (ExpressionEvalException e) {
return e.getMessage();
}
}
private static ThreadReference checkAndGetCurrentThread() {
Debugger debugger = Debugger.getInstance();
if (debugger.getVm() == null ) {
throw new ExpressionEvalException("no virtual machine connected.");
}
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
ThreadReference threadRef = threadStack.getCurThreadRef();
if (threadRef == null ) {
throw new ExpressionEvalException("no suspend thread.");
}
return threadRef;
}
private static String padStr(int maxLen, String origStr) {
if (origStr == null) return "";
int len = origStr.length();
if (len >= maxLen ) return origStr;
for (int i=0; i< (maxLen-len); i++) {
origStr = origStr + " ";
}
return origStr;
}
public static String fields() {
ThreadReference threadRef = checkAndGetCurrentThread();
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
StringBuilder sb = new StringBuilder();
try {
StackFrame stackFrame = threadRef.frame(threadStack.getCurFrame());
ObjectReference thisObj = stackFrame.thisObject();
Map<Field, Value> values = thisObj.getValues(thisObj.referenceType().visibleFields());
List<String> fieldNames = new ArrayList<String>();
for (Field field : values.keySet()) {
fieldNames.add(field.name());
}
int maxLen = getMaxLength(fieldNames)+2;
for (Field field : values.keySet()) {
sb.append(padStr(maxLen,field.name())).append(":");
sb.append(getPrettyPrintStr(values.get(field)));
sb.append("\n");
//stackFrame = threadRef.frame(0);
}
} catch (IncompatibleThreadStateException e) {
}
return sb.toString();
}
public static String variables() {
ThreadReference threadRef = checkAndGetCurrentThread();
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
int curFrame = threadStack.getCurFrame();
StringBuilder sb = new StringBuilder();
try {
List<String> varNames = new ArrayList<String>();
for (LocalVariable var : threadRef.frame(curFrame).visibleVariables()) {
varNames.add(var.name());
}
int maxLen = getMaxLength(varNames)+2;
for (LocalVariable var : threadRef.frame(curFrame).visibleVariables()) {
Value value = threadRef.frame(curFrame).getValue(var);
sb.append(padStr(maxLen,var.name())).append(":");
sb.append(getPrettyPrintStr(value));
sb.append("\n");
}
} catch (AbsentInformationException e) {
e.printStackTrace();
} catch (IncompatibleThreadStateException e) {
e.printStackTrace();
}
return sb.toString();
}
public static String reftype(String expXmlStr) {
List<Expression> exps = Expression.parseExpXmlStr(expXmlStr);
if (exps ==null || exps.size() == 0) return "";
Value value = getJdiValue(exps.get(0));
return value.type().name();
}
public static String inspect(String expXmlStr) {
List<Expression> exps = Expression.parseExpXmlStr(expXmlStr);
if (exps ==null || exps.size() == 0) return "";
Expression exp = exps.get(0);
Value value = getJdiValue(exp);
StringBuilder sb = new StringBuilder();
if (value instanceof ObjectReference) {
sb.append(exp.getOriExp()).append(" = ");
sb.append(value.type().name()).append("\n");
ObjectReference objRef = (ObjectReference) value;
Map<Field, Value> values = objRef.getValues(objRef.referenceType().visibleFields());
List<String> fieldNames = new ArrayList<String>();
for (Field field : values.keySet()) {
if (field.isStatic()) continue;
fieldNames.add(field.name());
}
int maxLen = getMaxLength(fieldNames)+2;
for (Field field : values.keySet()) {
sb.append(" ");
sb.append(padStr(maxLen,field.name())).append(":");
sb.append(getPrettyPrintStr(values.get(field)));
sb.append("\n");
}
}
return sb.toString();
}
public static String eval(String expXmlStr) {
List<Expression> exps = Expression.parseExpXmlStr(expXmlStr);
return eval(exps);
}
public static Value getJdiValue(Expression exp) {
ThreadReference threadRef = checkAndGetCurrentThread();
try {
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
StackFrame stackFrame = threadRef.frame(threadStack.getCurFrame());
ObjectReference thisObj = stackFrame.thisObject();
Value value = eval(threadRef, exp, thisObj,false);
return value;
} catch (IncompatibleThreadStateException e) {
throw new ExpressionEvalException("eval expression error, caused by : " + e.getMessage());
}
}
public static Value eval(ThreadReference threadRef, Expression exp,
ObjectReference thisObj,boolean hasParents) {
if (exp == null)
return null;
VirtualMachine vm = threadRef.virtualMachine();
String expType = exp.getExpType();
String expName = exp.getName();
if (expType.equals(Expression.EXP_TYPE_BOOL)) {
if (expName.equals("true"))
return vm.mirrorOf(true);
else
return vm.mirrorOf(false);
} else if (expType.equals(Expression.EXP_TYPE_STR)) {
return vm.mirrorOf(expName);
} else if (expType.equals(Expression.EXP_TYPE_NULL)) {
return null;
} else if (expType.equals(Expression.EXP_TYPE_NUM)) {
if (expName.indexOf(".") > -1) {
return vm.mirrorOf(Float.parseFloat(expName));
} else {
return vm.mirrorOf(Integer.parseInt(expName));
}
}
Value basicExpValue = null;
if (!exp.isMethod()) {
basicExpValue = findValueInFrame(threadRef, expName,thisObj,hasParents);
} else {
List<Expression> params = exp.getParams();
List<Value> arguments = new ArrayList<Value>();
if (params.size() != 0) {
for (Expression param : params) {
Value paramValue = eval(threadRef, param, thisObj,false);
arguments.add(paramValue);
}
}
basicExpValue = invoke((ObjectReference) thisObj, expName, arguments);
}
if (exp.isArrayExp()) {
if (basicExpValue instanceof ArrayReference) {
ArrayReference array = (ArrayReference)basicExpValue;
Value arrayIdxValue = eval(threadRef, exp.getArrayIdxExp(), thisObj,false);
if (arrayIdxValue instanceof IntegerValue ) {
int idx = ((IntegerValue)arrayIdxValue).value();
basicExpValue = array.getValue(idx);
} else {
throw new ExpressionEvalException("eval expression error, array index is not int type.");
}
}
}
List<Expression> members = exp.getMembers();
if (members.size() == 0)
return basicExpValue;
if (exp.isStaticMember()) {
Expression memberExp = members.get(0);
List<Expression> params = memberExp.getParams();
List<Value> arguments = new ArrayList<Value>();
if (params.size() != 0) {
for (Expression param : params) {
Value paramValue = eval(threadRef, param, thisObj,false);
arguments.add(paramValue);
}
}
List<ReferenceType> refTypes = vm.classesByName(exp.getName());
if (refTypes == null || refTypes.size() == 0) {
throw new ExpressionEvalException("eval expression error, type '" + exp.getName() + "' can't be found ");
}
basicExpValue = invoke(refTypes.get(0), memberExp.getName(), arguments);
if (members.size() > 1) {
for (int i = 1; i < members.size(); i++) {
Expression member = members.get(i);
basicExpValue = eval(threadRef, member, (ObjectReference) basicExpValue,true);
}
}
} else {
for (Expression member : members) {
basicExpValue = eval(threadRef, member,
(ObjectReference) basicExpValue,true);
}
}
return basicExpValue;
}
public static Value findValueInFrame(ThreadReference threadRef, String name,
ObjectReference thisObj, boolean hasParents) {
Value value = null;
try {
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
StackFrame stackFrame = threadRef.frame(threadStack.getCurFrame());
if (!hasParents) {
LocalVariable localVariable;
localVariable = stackFrame.visibleVariableByName(name);
if (localVariable != null) {
return stackFrame.getValue(localVariable);
}
}
ReferenceType refType = stackFrame.location().declaringType();
if (thisObj != null ) {
refType = thisObj.referenceType();
}
Field field = refType.fieldByName(name);
if (field == null ) {
throw new ExpressionEvalException("eval expression error, field '" + name +"' can't be found.");
}
if (thisObj != null) {
value = thisObj.getValue(field);
} else {
value = refType.getValue(field);
}
} catch (IncompatibleThreadStateException e) {
throw new ExpressionEvalException("eval expression error, caused by:" + e.getMessage());
} catch (AbsentInformationException e) {
throw new ExpressionEvalException("eval expression error, caused by:" + e.getMessage());
}
return value;
}
public static String getPrettyPrintStr(Value var) {
if (var == null)
return "null";
if (var instanceof ArrayReference) {
StringBuilder sb = new StringBuilder("[");
ArrayReference arrayObj = (ArrayReference) var;
if (arrayObj.length() == 0)
return "[]";
List<Value> values = arrayObj.getValues();
for (Value value : values) {
sb.append(getPrettyPrintStr(value)).append(",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
} else if (var instanceof ObjectReference) {
Value strValue = invoke((ObjectReference) var, "toString",
new ArrayList());
return strValue.toString();
} else {
return var.toString();
}
}
public static Value invoke(Object invoker, String methodName, List args) {
SuspendThreadStack threadStack = SuspendThreadStack.getInstance();
ThreadReference threadRef = threadStack.getCurThreadRef();
Value value = null;
Method matchedMethod = null;
List<Method> methods = null;
ClassType refType = null;
ObjectReference obj = null;
if (invoker instanceof ClassType) {
refType = (ClassType)invoker;
methods = refType.methodsByName(methodName);
} else {
obj = (ObjectReference)invoker;
methods = obj.referenceType().methodsByName(methodName);
}
if (methods == null || methods.size() == 0) {
throw new ExpressionEvalException("eval expression error, method '" + methodName + "' can't be found");
}
if (methods.size() == 1) {
matchedMethod = methods.get(0);
} else {
matchedMethod = findMatchedMethod(methods, args);
}
try {
if (invoker instanceof ClassType) {
ClassType clazz = (ClassType)refType;
value = clazz.invokeMethod(threadRef, matchedMethod, args,
ObjectReference.INVOKE_SINGLE_THREADED);
} else {
value = obj.invokeMethod(threadRef, matchedMethod, args,
ObjectReference.INVOKE_SINGLE_THREADED);
}
} catch (InvalidTypeException e) {
e.printStackTrace();
throw new ExpressionEvalException("eval expression error, caused by :" + e.getMessage());
} catch (ClassNotLoadedException e) {
e.printStackTrace();
throw new ExpressionEvalException("eval expression error, caused by :" + e.getMessage());
} catch (IncompatibleThreadStateException e) {
e.printStackTrace();
throw new ExpressionEvalException("eval expression error, caused by :" + e.getMessage());
} catch (InvocationException e) {
e.printStackTrace();
throw new ExpressionEvalException("eval expression error, caused by :" + e.getMessage());
}
return value;
}
private static Method findMatchedMethod(List<Method> methods, List arguments) {
for (Method method : methods) {
try {
List argTypes = method.argumentTypes();
if (argumentsMatch(argTypes, arguments))
return method;
} catch (ClassNotLoadedException e) {
}
}
return null;
}
private static boolean isPrimitiveType(String name) {
for (String primitiveType : primitiveTypeNames) {
if (primitiveType.equals(name))
return true;
}
return false;
}
private static boolean argumentsMatch(List argTypes, List arguments) {
if (argTypes.size() != arguments.size()) {
return false;
}
Iterator typeIter = argTypes.iterator();
Iterator valIter = arguments.iterator();
while (typeIter.hasNext()) {
Type argType = (Type) typeIter.next();
Value value = (Value) valIter.next();
if (value == null) {
if (isPrimitiveType(argType.name()))
return false;
}
if (!value.type().equals(argType)) {
if (isAssignableTo(value.type(), argType)) {
return true;
} else {
return false;
}
}
}
return true;
}
private static boolean isAssignableTo(Type fromType, Type toType) {
if (fromType.equals(toType))
return true;
if (fromType instanceof BooleanType && toType instanceof BooleanType)
return true;
if (toType instanceof BooleanType)
return false;
if (fromType instanceof PrimitiveType
&& toType instanceof PrimitiveType)
return true;
if (toType instanceof PrimitiveType)
return false;
if (fromType instanceof ArrayType) {
return isArrayAssignableTo((ArrayType) fromType, toType);
}
List interfaces;
if (fromType instanceof ClassType) {
ClassType superclazz = ((ClassType) fromType).superclass();
if ((superclazz != null) && isAssignableTo(superclazz, toType)) {
return true;
}
interfaces = ((ClassType) fromType).interfaces();
} else {
interfaces = ((InterfaceType) fromType).superinterfaces();
}
Iterator iter = interfaces.iterator();
while (iter.hasNext()) {
InterfaceType interfaze = (InterfaceType) iter.next();
if (isAssignableTo(interfaze, toType)) {
return true;
}
}
return false;
}
static boolean isArrayAssignableTo(ArrayType fromType, Type toType) {
if (toType instanceof ArrayType) {
try {
Type toComponentType = ((ArrayType) toType).componentType();
return isComponentAssignable(fromType.componentType(),
toComponentType);
} catch (ClassNotLoadedException e) {
return false;
}
}
if (toType instanceof InterfaceType) {
return toType.name().equals("java.lang.Cloneable");
}
return toType.name().equals("java.lang.Object");
}
private static boolean isComponentAssignable(Type fromType, Type toType) {
if (fromType instanceof PrimitiveType) {
return fromType.equals(toType);
}
if (toType instanceof PrimitiveType) {
return false;
}
return isAssignableTo(fromType, toType);
}
}