package com.laytonsmith.core.functions;
import com.laytonsmith.PureUtilities.Common.StreamUtils;
import com.laytonsmith.PureUtilities.HeapDumper;
import com.laytonsmith.annotations.api;
import com.laytonsmith.annotations.noboilerplate;
import com.laytonsmith.core.CHVersion;
import com.laytonsmith.core.LogLevel;
import com.laytonsmith.core.MethodScriptFileLocations;
import com.laytonsmith.core.Prefs;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.CArray;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.CVoid;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.IVariable;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREIOException;
import com.laytonsmith.core.exceptions.CRE.CREPluginInternalException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import java.io.File;
import java.io.IOException;
import java.util.Set;
/**
*
*/
public class Debug {
// public static boolean EVENT_LOGGING = false;
// public static int EVENT_LOGGING_LEVEL = 1;
// public static final Set<Event.Type> EVENT_LOGGING_FILTER = new HashSet<Event.Type>();
// public static final Set<String> EVENT_PLUGIN_FILTER = new HashSet<String>();
//public static boolean LOG_TO_SCREEN = false;
// public static void DoLog(Event.Type filter, int verbosity, String message) {
// synchronized (EVENT_LOGGING_FILTER) {
// if (EVENT_LOGGING && EVENT_LOGGING_FILTER.contains(filter) && EVENT_LOGGING_LEVEL >= verbosity) {
// try {
// Static.LogDebug(message);
// } catch (IOException ex) {
// Logger.getLogger(Debug.class.getVariableName()).log(Level.SEVERE, null, ex);
// }
// }
// }
// }
//
// public static boolean IsFiltered(Plugin plugin) {
// if (EVENT_PLUGIN_FILTER.isEmpty()) {
// return true;
// } else {
// return EVENT_PLUGIN_FILTER.contains(plugin.getClass().getSimpleName().toUpperCase());
// }
// }
public static String docs() {
return "Provides methods for viewing data about both CommandHelper and the other plugins in your server. Though not meant to"
+ " be called by normal scripts, these methods are available everywhere other methods are available. Note that for"
+ " some of these functions to even work, play-dirty mode must set to on. These are most useful in conjuction with"
+ " interpreter mode.";
}
// @api
// public static class dump_listeners extends AbstractFunction {
//
// public String getVariableName() {
// return "dump_listeners";
// }
//
// public Integer[] numArgs() {
// return new Integer[]{0, 1, 2};
// }
//
// public String docs() {
// return " {[typeFilter], [verboseLevel]} Send null as the typeFilter to see possibilities. VerboseLevel can be 1-4";
// }
//
// public Class<? extends CREThrowable>[] thrown() {
// return new Class[]{CRECastException.class, CRESecurityException.class};
// }
//
// public boolean isRestricted() {
// return true;
// }
//
// public boolean preResolveVariables() {
// return true;
// }
//
// public CHVersion since() {
// return "0.0.0";
// }
//
// public Boolean runAsync() {
// return false;
// }
//
// public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
// if (!(Boolean) Static.getPreferences().getPreference("allow-debug-logging")) {
// throw new ConfigRuntimeException("allow-debug-logging is currently set to false. To use " + this.getVariableName() + ", enable it in your preferences.", CRESecurityException.class, t);
// }
// StringBuilder b = new StringBuilder("\n");
// if (args.length >= 1 && args[0] instanceof CNull) {
// b.append("You can sort the listeners further by specifying one of the options:\n");
// for (Event.Type t : Event.Type.values()) {
// b.append(t.name()).append("\n");
// }
// return new CString(b.toString(), 0, null);
// }
// int verbosity = 1;
// if (args.length == 2) {
// verbosity = Static.getInt32(args[1]);
// }
// try {
// SimplePluginManager pm = (SimplePluginManager) AliasCore.parent.getServer().getPluginManager();
// Field fListener = SimplePluginManager.class.getDeclaredField("listeners");
// //set it to public
// fListener.setAccessible(true);
// EnumMap<Event.Type, SortedSet<RegisteredListener>> listeners =
// (EnumMap<Event.Type, SortedSet<RegisteredListener>>) fListener.get(pm);
//
// if (args.length >= 1) {
// for (RegisteredListener l : listeners.get(Event.Type.valueOf(args[0].val().toUpperCase()))) {
// b.append(Build(l, verbosity));
// }
// } else {
// for (Event.Type type : listeners.keySet()) {
// b.append("Type: ").append(type.name()).append("\n");
// for (RegisteredListener l : listeners.get(type)) {
// b.append(Build(l, verbosity));
// }
// }
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return new CString(b.toString(), 0, null);
// }
//
// public String Build(RegisteredListener l, int verbosity) {
// StringBuilder b = new StringBuilder();
// switch (Static.Normalize(verbosity, 1, 5)) {
// case 1:
// b.append("Plugin: ").append(l.getPlugin().getClass().getSimpleName()).append("; Priority: ").append(l.getPriority().toString()).append("\n");
// break;
// case 2:
// b.append("Plugin: ").append(l.getPlugin().getClass().getSimpleName()).append(":").append(l.getListener().getClass().getSimpleName()).append("; Priority: ").append(l.getPriority().toString()).append("\n");
// break;
// case 3:
// b.append("Plugin: ").append(l.getPlugin().getClass().getSimpleName()).append(":").append(l.getListener().getClass().getCanonicalName()).append("; Priority: ").append(l.getPriority().toString()).append("\n");
// break;
// case 4:
// b.append("Plugin: ").append(l.getPlugin().getClass().getSimpleName()).append(":").append(l.getListener().getClass().getCanonicalName()).append("\n\t").append("; Priority: ").append(l.getPriority().toString()).append("\n");
// break;
// case 5:
// b.append("Plugin: ").append(l.getPlugin().getClass().getSimpleName()).append(":").append(l.getListener().getClass().getCanonicalName()).append("; Priority: ").append(l.getPriority().toString()).append("\n");
// b.append("\tMethods defined in listener that override ");
// try {
// Class<? extends Listener> parent = (Class<? extends Listener>) l.getListener().getClass().getSuperclass();
// while (parent.getSuperclass() != null && parent.getSuperclass().equals(Listener.class)) {
// parent = (Class<? extends Listener>) parent.getSuperclass();
// }
// b.append(parent.getSimpleName()).append(":\n");
// Set<Method> parentSet = new HashSet(Arrays.asList(parent.getDeclaredMethods()));
// for (Method m : l.getListener().getClass().getDeclaredMethods()) {
// for (Method pm : parentSet) {
// if (pm.getVariableName().equals(m.getVariableName()) && Arrays.equals(pm.getParameterTypes(), m.getParameterTypes())) {
// b.append("\t\t").append(m.getReturnType().getSimpleName()).append(" ").append(m.getVariableName()).append("(").append(Static.strJoin(m.getParameterTypes(), ", ")).append(");\n");
// }
// }
// }
// } catch (NoClassDefFoundError e) {
// b.append("Could not get methods for ").append(l.getListener().getClass());
// }
// break;
// }
// return b.toString();
// }
//
// public String BuildClassList(Class[] list) {
// StringBuilder b = new StringBuilder();
// ArrayList<String> l = new ArrayList<String>();
// for (Class c : list) {
// try {
// l.add(c.getSimpleName());
// } catch (NoClassDefFoundError e) {
// }
// }
// return Static.strJoin(list, ", ");
// }
// }
@api(environments={GlobalEnv.class})
public static class debug extends AbstractFunction {
@Override
public String getName() {
return "debug";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "void {message} Manually logs a timestamped message to the debug log and the console, if debug-mode is set to true in the preferences";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREIOException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return true;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
if (Prefs.DebugMode()) {
try {
Static.LogDebug(MethodScriptFileLocations.getDefault().getConfigDirectory(), args[0].val(), LogLevel.DEBUG);
} catch (IOException ex) {
throw new CREIOException(ex.getMessage(), t, ex);
}
}
return CVoid.VOID;
}
}
@api
public static class trace extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
if(args[0] instanceof IVariable){
if(Prefs.DebugMode()){
IVariable ivar = (IVariable)args[0];
Construct val = environment.getEnv(GlobalEnv.class).GetVarList().get(ivar.getVariableName(), t);
StreamUtils.GetSystemOut().println(ivar.getVariableName() + ": " + val.val());
}
return CVoid.VOID;
} else {
throw new CRECastException("Expecting an ivar, but recieved " + args[0].getCType() + " instead", t);
}
//TODO: Once Prefs are no longer static, check to see if debug mode is on during compilation, and
//if so, remove this function entirely
}
@Override
public boolean preResolveVariables() {
return false;
}
@Override
public String getName() {
return "trace";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "void {ivar} If debug mode is on, outputs debug information about a variable. Unlike debug, this only accepts an ivar; it is a meta function."
+ " The runtime will then take the variable, and output information about it, in a human readable format, including"
+ " the variable's name and value. If debug mode is off, the function is ignored.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
// @api
// public static class debug_log_events extends AbstractFunction {
//
// public String getVariableName() {
// return "debug_log_events";
// }
//
// public Integer[] numArgs() {
// return new Integer[]{1, 2, 3};
// }
//
// public String docs() {
// return "void {boolean, [level, [logToScreen]]} Turns the event logging on or off. Event logging may be useful in determining the problem if CommandHelper isn't"
// + " able to receive events, you can track what's actually happening. play-dirty mode must be enabled for this to work properly however."
// + " This feature may also be useful in diagnosing other problems with other plugins as well. Level varies from 1-5, and shows more"
// + " information as it increases. You must also set at least one filter with the set_debug_event_filter function before anything"
// + " will happen. logToScreen defaults to false. This should only be turned on when you are testing, or have very strict filters set.";
// }
//
// public Class<? extends CREThrowable>[] thrown() {
// return new Class[]{CRECastException.class, CRESecurityException.class};
// }
//
// public boolean isRestricted() {
// return true;
// }
//
// public boolean preResolveVariables() {
// return true;
// }
//
// public CHVersion since() {
// return CHVersion.V3_3_0;
// }
//
// public Boolean runAsync() {
// return false;
// }
//
// public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
// if (!(Boolean) Static.getPreferences().getPreference("allow-debug-logging")) {
// throw new ConfigRuntimeException("allow-debug-logging is currently set to false. To use " + this.getVariableName() + ", enable it in your preferences.", CRESecurityException.class, t);
// }
// boolean on = Static.getBoolean(args[0]);
// int level = 1;
// if(args.length >= 2){
// level = Static.Normalize(Static.getInt32(args[1]), 1, 5);
// }
// Debug.EVENT_LOGGING = on;
// Debug.EVENT_LOGGING_LEVEL = level;
// if(args.length >= 3){
// Debug.LOG_TO_SCREEN = Static.getBoolean(args[2]);
// }
// return CVoid.VOID;
// }
// }
// @api
// public static class set_debug_event_filter extends AbstractFunction {
//
// public String getVariableName() {
// return "set_debug_event_filter";
// }
//
// public Integer[] numArgs() {
// return new Integer[]{1};
// }
//
// public String docs() {
// return "void {array} Logs the specified event types as they occur, assuming that logging is currently enabled. For a list of"
// + " available filters, you can run dump_listeners(null). As these events occur, they will be logged according to the logging level.";
// }
//
// public Class<? extends CREThrowable>[] thrown() {
// return new Class[]{CRECastException.class, CREFormatException.class, CRESecurityException.class};
// }
//
// public boolean isRestricted() {
// return true;
// }
//
// public boolean preResolveVariables() {
// return true;
// }
//
// public CHVersion since() {
// return CHVersion.V3_3_0;
// }
//
// public Boolean runAsync() {
// return true;
// }
//
// public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
// if (!(Boolean) Static.getPreferences().getPreference("allow-debug-logging")) {
// throw new ConfigRuntimeException("allow-debug-logging is currently set to false. To use " + this.getVariableName() + ", enable it in your preferences.", CRESecurityException.class, t);
// }
// Set<Event.Type> set = new HashSet<Event.Type>();
// if (args[0] instanceof CString) {
// if (args[0].val().equals("*")) {
// for (Event.Type t : Event.Type.values()) {
// set.add(t);
// }
// } else {
// try {
// Event.Type t = Event.Type.valueOf(args[0].val().toUpperCase());
// set.add(t);
// } catch (IllegalArgumentException e) {
// throw new ConfigRuntimeException(args[0].val() + " is not a valid filter type. The filter log has not been changed.", CREFormatException.class, t);
// }
// }
// } else if (args[0] instanceof CArray) {
// for (String c : ((CArray) args[0]).keySet()) {
// try {
// set.add(Event.Type.valueOf(((CArray) args[0]).get(c, t).val().toUpperCase()));
// } catch (IllegalArgumentException e) {
// throw new ConfigRuntimeException(c + " is not a valid filter type. The filter log has not been changed.", CREFormatException.class, t);
// }
// }
// } else {
// throw new ConfigRuntimeException("The parameter specified to " + this.getVariableName() + " must be an array (or a single string). The filter array has not been changed.", CRECastException.class, t);
// }
// synchronized (EVENT_LOGGING_FILTER) {
// EVENT_LOGGING_FILTER.clear();
// for (Event.Type t : set) {
// EVENT_LOGGING_FILTER.add(t);
// }
// }
// return CVoid.VOID;
// }
// }
// @api
// public static class set_debug_plugin_filter extends AbstractFunction {
//
// public String getVariableName() {
// return "set_debug_plugin_filter";
// }
//
// public Integer[] numArgs() {
// return new Integer[]{1};
// }
//
// public String docs() {
// return "void {array} Often times you just are interested in the events a particular plugin is outputting. If the plugin filter"
// + " is empty, all plugins are reported (assuming their event types are not filtered out) otherwise, only the ones in"
// + " the list are logged. The name of the plugin is the field \"Called from Plugin: \" in the output, not the name"
// + " it may be commonly referred to as.";
// }
//
// public Class<? extends CREThrowable>[] thrown() {
// return new Class[]{CRECastException.class, CRESecurityException.class};
// }
//
// public boolean isRestricted() {
// return true;
// }
//
// public boolean preResolveVariables() {
// return true;
// }
//
// public CHVersion since() {
// return CHVersion.V3_3_0;
// }
//
// public Boolean runAsync() {
// return false;
// }
//
// public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
// if (!(Boolean) Static.getPreferences().getPreference("allow-debug-logging")) {
// throw new ConfigRuntimeException("allow-debug-logging is currently set to false. To use " + this.getVariableName() + ", enable it in your preferences.", CRESecurityException.class, t);
// }
// if (args[0] instanceof CString) {
// EVENT_PLUGIN_FILTER.clear();
// EVENT_PLUGIN_FILTER.add(args[0].val().toUpperCase());
// } else if (args[0] instanceof CArray) {
// for (String c : ((CArray) args[0]).keySet()) {
// EVENT_PLUGIN_FILTER.add(((CArray) args[0]).get(c, t).val().toUpperCase());
// }
// } else {
// throw new ConfigRuntimeException(this.getVariableName() + " expects the argument to be a single string, or an array of strings.", CRECastException.class, t);
// }
// return CVoid.VOID;
// }
// }
@api
public static class dump_threads extends AbstractFunction{
@Override
public Class<? extends CREThrowable>[] thrown() {
return null;
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
CArray carray = new CArray(t);
for(Thread thread : threadSet){
carray.push(new CString(thread.getName(), t), t);
}
return carray;
}
@Override
public String getName() {
return "dump_threads";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public String docs() {
return "array {} Returns an array of all thread names that are currently running in the JVM."
+ " This is a debugging tool for your server, and less of a CommandHelper specific thing.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
@noboilerplate
public static class heap_dump extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPluginInternalException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
File file = new File("dump.bin");
try{
HeapDumper.dumpHeap(file.getAbsolutePath(), true);
} catch(Throwable tt){
throw new CREPluginInternalException("Could not create a heap dump: " + tt.getMessage(), t, tt);
}
return CVoid.VOID;
}
@Override
public String getName() {
return "heap_dump";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public String docs() {
return "void {} Creates a heap dump file, and places it in the working directory, as \"dump.bin\". This might"
+ " throw a PluginInternalException if the heap dump tools aren't available in your JVM. Once dumped,"
+ " the heap dump can be analyzed using tools such as jhat. More information about jhat can be found"
+ " [http://docs.oracle.com/javase/6/docs/technotes/tools/share/jhat.html here].";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
}