Skip to content

Commit

Permalink
Use new Java StackWalker API (#87)
Browse files Browse the repository at this point in the history
* Use new Java `StackWalker` API

* `Set<Object>` -> `HashSet<Object>`

* Only skip 2 and then filter through the rest
  • Loading branch information
tal5 committed Feb 18, 2023
1 parent 1c4d56f commit 6a8cc6e
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 86 deletions.
@@ -1,7 +1,10 @@
package com.denizenscript.denizencore.tags.core;

import com.denizenscript.denizencore.DenizenCore;
import com.denizenscript.denizencore.events.core.TickScriptEvent;
import com.denizenscript.denizencore.objects.*;
import com.denizenscript.denizencore.objects.ArgumentHelper;
import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.*;
import com.denizenscript.denizencore.objects.notable.Notable;
import com.denizenscript.denizencore.objects.notable.NoteManager;
Expand All @@ -12,10 +15,10 @@
import com.denizenscript.denizencore.scripts.commands.queue.RunLaterCommand;
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.queues.ScriptQueue;
import com.denizenscript.denizencore.tags.*;
import com.denizenscript.denizencore.tags.PseudoObjectTagBase;
import com.denizenscript.denizencore.tags.TagManager;
import com.denizenscript.denizencore.utilities.*;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import com.denizenscript.denizencore.DenizenCore;
import com.denizenscript.denizencore.utilities.debugging.DebugInternals;

import java.io.File;
Expand Down Expand Up @@ -984,11 +987,8 @@ else if (attribute.startsWith("format", 2) && attribute.hasParam()) {
// Class names may appear multiple times in a row if that class contains methods that call each other.
// -->
tagProcessor.registerTag(ListTag.class, "java_class_context", (attribute, object) -> {
ListTag list = new ListTag();
for (Class<?> clazz : DebugInternals.getClassContext()) {
list.addObject(new ElementTag(clazz.getName(), true));
}
return list;
return new ListTag((Collection<? extends ObjectTag>) StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(stackFrameStream ->
stackFrameStream.map(stackFrame -> new ElementTag(stackFrame.getClassName(), true)).toList()));
});
}

Expand Down
Expand Up @@ -16,6 +16,7 @@
import com.denizenscript.denizencore.utilities.CoreUtilities;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
Expand Down Expand Up @@ -108,33 +109,33 @@ public static void echoErrorInternal(ScriptEntry source, String addedContext, St
}
HashSet<Object> duplicatePrevention = new HashSet<>();
for (Object context : Debug.errorContextStack) {
if (context instanceof ScriptTag) {
context = ((ScriptTag) context).getContainer();
if (context instanceof ScriptTag scriptTag) {
context = scriptTag.getContainer();
}
else if (context instanceof QueueTag) {
context = ((QueueTag) context).getQueue();
else if (context instanceof QueueTag queueTag) {
context = queueTag.getQueue();
}
else if (context instanceof Supplier<?>) {
context = ((Supplier<?>) context).get();
else if (context instanceof Supplier<?> supplier) {
context = supplier.get();
}
if (!duplicatePrevention.add(context)) {
continue;
}
if (context instanceof ScriptContainer) {
if (context instanceof ScriptContainer container) {
if (sourceScript == null || context != sourceScript.getContainer()) {
headerBuilder.append(" in script '<A>").append(((ScriptContainer) context).getName()).append("<LR>'");
headerBuilder.append(" in script '<A>").append(container.getName()).append("<LR>'");
}
}
else if (context instanceof ScriptQueue) {
else if (context instanceof ScriptQueue queue) {
if (context != sourceQueue) {
headerBuilder.append(" in queue '").append(((ScriptQueue) context).debugId).append("<LR>'");
headerBuilder.append(" in queue '").append(queue.debugId).append("<LR>'");
}
}
else if (context instanceof String) {
headerBuilder.append(" ").append((String) context);
else if (context instanceof String str) {
headerBuilder.append(" ").append(str);
}
else if (context instanceof ObjectTag) {
headerBuilder.append(((ObjectTag) context).getErrorHeaderContext());
else if (context instanceof ObjectTag objectTag) {
headerBuilder.append(objectTag.getErrorHeaderContext());
}
else if (context != null) {
headerBuilder.append("\n<FORCE_ALIGN>With non-context-valid object '<A>").append(context).append("<LR>'");
Expand Down Expand Up @@ -191,9 +192,9 @@ public static String getFullExceptionMessage(Throwable ex, boolean includeBoundi
if (!first) {
errorMessage.append("Caused by: ");
}
errorMessage.append(ex).append("\n");
errorMessage.append(ex).append('\n');
for (StackTraceElement ste : ex.getStackTrace()) {
errorMessage.append(prefix).append(" ").append(ste.toString()).append("\n");
errorMessage.append(prefix).append(" ").append(ste).append('\n');
}
if (ex.getCause() == ex) {
break;
Expand Down Expand Up @@ -256,18 +257,14 @@ public static Consumer<String> getDebugSender(Debuggable caller) {
if (caller == null) {
caller = CommandExecutor.currentQueue;
}
if (caller instanceof TagContext) {
if (((TagContext) caller).entry != null) {
caller = ((TagContext) caller).entry;
}
if (caller instanceof TagContext context && context.entry != null) {
caller = context.entry;
}
if (caller instanceof ScriptEntry) {
if (((ScriptEntry) caller).getResidingQueue() != null) {
caller = ((ScriptEntry) caller).getResidingQueue();
}
if (caller instanceof ScriptEntry entry && entry.getResidingQueue() != null) {
caller = entry.getResidingQueue();
}
if (caller instanceof ScriptQueue) {
return ((ScriptQueue) caller).debugOutput;
if (caller instanceof ScriptQueue queue) {
return queue.debugOutput;
}
if (specialBackupSender != null) {
return specialBackupSender;
Expand All @@ -286,37 +283,37 @@ public static void echo(String string, Debuggable caller) {
return;
}
String callerId;
if (caller instanceof ScriptContainer) {
callerId = "Script:" + ((ScriptContainer) caller).getName();
if (caller instanceof ScriptContainer container) {
callerId = "Script:" + container.getName();
}
else if (caller instanceof ScriptEntry) {
if (((ScriptEntry) caller).getScript() != null) {
callerId = "Command:" + ((ScriptEntry) caller).getCommandName() + " in Script:" + ((ScriptEntry) caller).getScript().getName();
else if (caller instanceof ScriptEntry entry) {
if (entry.getScript() != null) {
callerId = "Command:" + entry.getCommandName() + " in Script:" + entry.getScript().getName();
}
else {
callerId = "Command:" + ((ScriptEntry) caller).getCommandName();
callerId = "Command:" + entry.getCommandName();
}
}
else if (caller instanceof ScriptQueue) {
if (((ScriptQueue) caller).script != null) {
callerId = "Queue:" + ((ScriptQueue) caller).id + " running Script:" + ((ScriptQueue) caller).script.getName();
else if (caller instanceof ScriptQueue queue) {
if (queue.script != null) {
callerId = "Queue:" + queue.id + " running Script:" + queue.script.getName();
}
else {
callerId = "Queue:" + ((ScriptQueue) caller).id;
callerId = "Queue:" + queue.id;
}
}
else if (caller instanceof TagContext) {
if (((TagContext) caller).entry != null) {
ScriptEntry sent = ((TagContext) caller).entry;
else if (caller instanceof TagContext context) {
if (context.entry != null) {
ScriptEntry sent = context.entry;
if (sent.getScript() != null) {
callerId = "Tag in Command:" + sent.getCommandName() + " in Script:" + sent.getScript().getName();
}
else {
callerId = "Tag in Command:" + sent.getCommandName();
}
}
else if (((TagContext) caller).script != null) {
callerId = "Tag in Script:" + ((TagContext) caller).script.getName();
else if (context.script != null) {
callerId = "Tag in Script:" + context.script.getName();
}
else {
callerId = "Tag:" + caller;
Expand Down Expand Up @@ -357,8 +354,7 @@ public static void finalOutputDebugText(String message, Debuggable caller, boole
else {
skipFooter = false;
}
message = DenizenCore.implementation.applyDebugColors(message);
internalFinalOutputPath(message, caller, reformat);
internalFinalOutputPath(DenizenCore.implementation.applyDebugColors(message), caller, reformat);
}

/** Internal final debug output called. Should generally not be called directly - instead use echoDebug, log, echoError, ... */
Expand Down Expand Up @@ -392,7 +388,7 @@ public static void internalFinalOutputPath(String message, Debuggable caller, bo
try {
// "HH:mm:ss"
String toRecord = " " + formatted.replace("<FORCE_ALIGN>", " ")+ "\n";
Debug.debugRecording.append(URLEncoder.encode(debugRecordDateFormat.format(new Date()) + toRecord, "UTF-8"));
Debug.debugRecording.append(URLEncoder.encode(debugRecordDateFormat.format(new Date()) + toRecord, StandardCharsets.UTF_8));
}
catch (Throwable ex) {
Debug.echoError(ex);
Expand Down Expand Up @@ -424,61 +420,39 @@ public static void internalFinalOutputPath(String message, Debuggable caller, bo
/** Used for "log" to get class names. */
public static final Map<Class<?>, String> classNameCache = new WeakHashMap<>();

/** Used for "log" to get class names. This class janks access to SecurityManager to be open to the DebugInternals class.
* Note: the format of this code is extremely unstable and jank as it relates to Java security internals stuff, do not touch unless you 100% know what you're doing. */
public static class SecurityManagerTrick extends SecurityManager {
@Override
@SuppressWarnings("rawtypes")
protected Class[] getClassContext() {
return super.getClassContext();
}
}

/** Helper to get the current class context. */
public static Class[] getClassContext() {
return new SecurityManagerTrick().getClassContext();
}

/** Used for "log" to get class names. */
public static boolean canGetClass = true;

/** Helper to get a class name with less JVM overhead using a cache. */
public static String getClassNameOpti(Class<?> clazz) {
String className = classNameCache.get(clazz);
if (className == null) {
classNameCache.put(clazz, className = clazz.getSimpleName());
}
return className;
return classNameCache.computeIfAbsent(clazz, k -> clazz.getSimpleName());
}

/** Helper to get the calling class for a 'log' message. */
public static Class<?> getCallerClass() {
/** Helper to get the caller for a 'log' message. */
public static StackWalker.StackFrame getCallerStackFrame() {
if (!canGetClass) {
return null;
}
try {
if (canGetClass) {
Class[] classes = new SecurityManagerTrick().getClassContext();
for (int i = 2; i < classes.length; i++) {
if (classes[i] != DebugInternals.class && classes[i] != Debug.class) {
return classes[i];
}
}
}
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk(stackFrameStream -> stackFrameStream.skip(2)
.dropWhile(stackFrame -> {
Class<?> declaringClass = stackFrame.getDeclaringClass();
return declaringClass == DebugInternals.class || declaringClass == Debug.class;
}).findFirst().orElse(null));
}
catch (Throwable ex) {
canGetClass = false;
return null;
}
return null;
}

/** Helper to get the name of the calling class for a 'log' message. */
public static String getCaller() {
Class<?> caller = getCallerClass();
if (caller == null) {
StackWalker.StackFrame callerStackFrame = getCallerStackFrame();
if (callerStackFrame == null) {
return "<JVM-Block>";
}
String callerName = getClassNameOpti(caller);
String callerName = getClassNameOpti(callerStackFrame.getDeclaringClass());
return callerName.length() > 16 ? callerName.substring(0, 12) + "..." : callerName;
}

Expand Down

0 comments on commit 6a8cc6e

Please sign in to comment.