/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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
* published by the Free Software Foundation.
*
* This code 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 the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* 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,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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
* questions.
*/
package com.sun.max.ins.debug.vmlog;
import java.awt.*;
import java.lang.reflect.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.value.*;
import com.sun.max.ins.value.WordValueLabel.ValueMode;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.log.VMLog.Record;
import com.sun.max.vm.log.*;
import com.sun.max.vm.thread.*;
/**
* Attempts to divine the types of the arguments based on the convention that
* a logger will define a method {@code logXXX(...)} for the operation
* defined as the enum constant {@code Operation.XXX}.
*/
public class DefaultVMLogArgRenderer extends VMLogArgRenderer {
private final PlainLabel BEGIN_LABEL = new PlainLabel(vmLogView.inspection(), VMLogger.Interval.BEGIN.name());
private final PlainLabel END_LABEL = new PlainLabel(vmLogView.inspection(), VMLogger.Interval.END.name());
public DefaultVMLogArgRenderer(VMLogView vmLogView) {
super(vmLogView);
}
private static class OperationLogger {
Class<?> operationClass;
Class<?> loggerClass;
OperationLogger(Class<?> operationClass, Class<?> loggerClass) {
this.operationClass = operationClass;
this.loggerClass = loggerClass;
}
}
@Override
protected Component getRenderer(int header, int argNum, long argValue) {
int op = Record.getOperation(header);
VMLogger vmLogger = vmLogView.vmLog().getLogger(Record.getLoggerId(header));
String inspectedArg = vmLogger.inspectedArgValue(op, argNum, Address.fromLong(argValue));
if (inspectedArg != null) {
return new PlainLabel(vmLogView.inspection(), inspectedArg);
}
OperationLogger operationDefiningClass = getOperationDefiningClass(vmLogger);
if (operationDefiningClass != null) {
Enum[] enums = (Enum[]) operationDefiningClass.operationClass.getEnumConstants();
if (enums != null) {
if (op < enums.length) {
Enum e = enums[op];
Class<?>[] types = getParameterTypes(operationDefiningClass.loggerClass, e.name());
if (types == null) {
TeleError.unexpected("failed to get parameter types for log" + e.name());
}
if (argNum <= types.length) {
return getRenderer(types[argNum - 1], argValue);
}
}
}
}
return defaultRenderer(argValue);
}
private Component getRenderer(Class klass, long argValue) {
if (Hub.class.isAssignableFrom(klass)) {
// currently ClassID of Hub.classActor
return safeGetReferenceValueLabel(getTeleClassActor(argValue));
} else if (ClassActor.class.isAssignableFrom(klass)) {
return safeGetReferenceValueLabel(getTeleClassActor(argValue));
} else if (ClassMethodActor.class.isAssignableFrom(klass)) {
return safeGetReferenceValueLabel(getTeleClassMethodActor(argValue));
} else if (MethodActor.class.isAssignableFrom(klass)) {
return safeGetReferenceValueLabel(getTeleMethodActor(argValue));
} else if (klass == VmThread.class) {
return VMLogView.ThreadCellRenderer.getThreadRenderer((int) argValue);
} else if (klass == VMLogger.Interval.class) {
return argValue == 0 ? BEGIN_LABEL : END_LABEL;
} else if (klass == int.class || klass == byte.class || klass == short.class || klass == long.class) {
return new PlainLabel(vmLogView.inspection(), String.valueOf(argValue));
} else if (klass == boolean.class) {
return new PlainLabel(vmLogView.inspection(), argValue == 0 ? "false" : "true");
} else {
Method inspectedValueMethod = getInspectedValueMethod(klass);
if (inspectedValueMethod != null) {
try {
return new PlainLabel(vmLogView.inspection(), (String) inspectedValueMethod.invoke(null, Address.fromLong(argValue)));
} catch (Exception ex) {
}
}
if (klass == Pointer.class || klass == Address.class) {
return new WordValueLabel(vmLogView.inspection(), ValueMode.WORD, Address.fromLong(argValue), vmLogView.getTable());
} else if (Object.class.isAssignableFrom(klass)) {
return new WordValueLabel(vmLogView.inspection(), ValueMode.REFERENCE, Address.fromLong(argValue), vmLogView.getTable());
}
}
return defaultRenderer(argValue);
}
PlainLabel defaultRenderer(long argValue) {
return new PlainLabel(inspection(), Long.toHexString(argValue));
}
private OperationLogger getOperationDefiningClass(VMLogger vmLogger) {
Class< ? > klass = vmLogger.getClass();
while (klass != null) {
Class< ? >[] declaredClasses = klass.getDeclaredClasses();
for (Class declaredClass : declaredClasses) {
if (declaredClass.isEnum() && declaredClass.getSimpleName().equals("Operation")) {
return new OperationLogger(declaredClass, klass);
}
}
klass = klass.getSuperclass();
}
return null;
}
private Class<?>[] getParameterTypes(Class<?> vmLoggerClass, String name) {
Method[] methods = vmLoggerClass.getDeclaredMethods();
String logName = "log" + name;
for (Method method : methods) {
if (method.getName().equals(logName)) {
return method.getParameterTypes();
}
}
return null;
}
private Method getInspectedValueMethod(Class<?> klass) {
Method[] methods = klass.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals("inspectedValue")) {
Class<?>[] params = method.getParameterTypes();
if (params.length == 1 && params[0].getSimpleName().equals("Word")) {
return method;
}
}
}
return null;
}
}