From 83a1f7200e3bde1aa0cfefac2abdc8873aefcbfe Mon Sep 17 00:00:00 2001 From: Dragon-Seeker Date: Mon, 11 Nov 2024 15:56:35 -0600 Subject: [PATCH] Adjust how throwables and stack trace are preserved --- .../wispforest/owo/mixin/DataResultMixin.java | 63 +++++++++++++++++-- .../owo/serialization/CodecUtils.java | 3 +- .../owo/util/StackTraceSupplier.java | 45 ++++++++++++- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/wispforest/owo/mixin/DataResultMixin.java b/src/main/java/io/wispforest/owo/mixin/DataResultMixin.java index 52e8d1ef..698daca9 100644 --- a/src/main/java/io/wispforest/owo/mixin/DataResultMixin.java +++ b/src/main/java/io/wispforest/owo/mixin/DataResultMixin.java @@ -1,17 +1,25 @@ package io.wispforest.owo.mixin; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import com.mojang.logging.LogUtils; import com.mojang.serialization.DataResult; +import com.mojang.serialization.Lifecycle; import io.wispforest.owo.Owo; import io.wispforest.owo.util.StackTraceSupplier; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -35,7 +43,7 @@ private static void wrapMessageWithStacktrace(CallbackInfoReturnable void wrapMessageWithStacktrace(CallbackInfoReturnable { + + @Unique + private static final Logger LOGGER = LogUtils.getLogger(); + @Shadow(remap = false) public abstract Supplier messageSupplier(); + @Shadow @Final + private Supplier messageSupplier; + @Inject(method = {"getOrThrow", "getPartialOrThrow"}, at = @At(value = "HEAD"), remap = false) private void addStackTraceToException(CallbackInfoReturnable cir, @Local(argsOnly = true) LocalRef> exceptionSupplier) { final var funcToWrap = exceptionSupplier.get(); @@ -64,11 +83,45 @@ private void addStackTraceToException(CallbackInfoReturnab exceptionSupplier.set(s -> { var exception = funcToWrap.apply(s); if (this.messageSupplier() instanceof StackTraceSupplier stackTraceSupplier) { - exception.setStackTrace(stackTraceSupplier.stackTrace()); + exception.setStackTrace(stackTraceSupplier.getFullStackTrace()); } - return exception; }); } + + @WrapOperation(method ={ + "resultOrPartial(Ljava/util/function/Consumer;)Ljava/util/Optional;", + "promotePartial" + }, at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V")) + private void printStackTrace(Consumer instance, T t, Operation original) { + original.call(instance, t); + + if (Owo.DEBUG && this.messageSupplier instanceof StackTraceSupplier supplier) { + LOGGER.error("An error has occurred within DFU: ", supplier.throwable()); + } + } + + @WrapOperation(method = { + "ap(Lcom/mojang/serialization/DataResult;)Lcom/mojang/serialization/DataResult$Error;", + "flatMap(Ljava/util/function/Function;)Lcom/mojang/serialization/DataResult$Error;" + }, at = @At(value = "NEW", target = "(Ljava/util/function/Supplier;Ljava/util/Optional;Lcom/mojang/serialization/Lifecycle;)Lcom/mojang/serialization/DataResult$Error;", ordinal = 1)) + private DataResult.Error preserveStackTrace1(Supplier messageSupplier, Optional partialValue, Lifecycle lifecycle, Operation original) { + if (this.messageSupplier instanceof StackTraceSupplier supplier) { + messageSupplier = StackTraceSupplier.of(supplier.throwable(), messageSupplier); + } + + return original.call(messageSupplier, partialValue, lifecycle); + } + + @WrapOperation(method = { + "mapError(Ljava/util/function/UnaryOperator;)Lcom/mojang/serialization/DataResult$Error;" + }, at = @At(value = "NEW", target = "(Ljava/util/function/Supplier;Ljava/util/Optional;Lcom/mojang/serialization/Lifecycle;)Lcom/mojang/serialization/DataResult$Error;")) + private DataResult.Error preserveStackTrace2(Supplier messageSupplier, Optional partialValue, Lifecycle lifecycle, Operation original) { + if (this.messageSupplier instanceof StackTraceSupplier supplier) { + messageSupplier = StackTraceSupplier.of(supplier.throwable(), messageSupplier); + } + + return original.call(messageSupplier, partialValue, lifecycle); + } } } diff --git a/src/main/java/io/wispforest/owo/serialization/CodecUtils.java b/src/main/java/io/wispforest/owo/serialization/CodecUtils.java index 33a777ad..e5eb2697 100644 --- a/src/main/java/io/wispforest/owo/serialization/CodecUtils.java +++ b/src/main/java/io/wispforest/owo/serialization/CodecUtils.java @@ -29,6 +29,7 @@ import io.wispforest.owo.serialization.format.nbt.NbtEndec; import io.wispforest.owo.serialization.format.nbt.NbtSerializer; import io.wispforest.owo.util.Scary; +import io.wispforest.owo.util.StackTraceSupplier; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtOps; @@ -434,7 +435,7 @@ private static DataResult captureThrows(Supplier action) { try { return DataResult.success(action.get()); } catch (Exception e) { - return DataResult.error(e::getMessage); + return DataResult.error(StackTraceSupplier.of(e)); } } diff --git a/src/main/java/io/wispforest/owo/util/StackTraceSupplier.java b/src/main/java/io/wispforest/owo/util/StackTraceSupplier.java index d201b765..e4e45a19 100644 --- a/src/main/java/io/wispforest/owo/util/StackTraceSupplier.java +++ b/src/main/java/io/wispforest/owo/util/StackTraceSupplier.java @@ -1,10 +1,51 @@ package io.wispforest.owo.util; +import org.jetbrains.annotations.Nullable; + import java.util.function.Supplier; -public record StackTraceSupplier(StackTraceElement[] stackTrace, Supplier message) implements Supplier { +public final class StackTraceSupplier implements Supplier { + private final Throwable throwable; + private final @Nullable Supplier message; + + private StackTraceSupplier(@Nullable Throwable throwable, @Nullable Supplier message) { + this.throwable = throwable; + this.message = message; + } + + public static StackTraceSupplier of(Throwable throwable) { + return new StackTraceSupplier(throwable, null); + } + + public static StackTraceSupplier of(Throwable throwable, Supplier supplier) { + return new StackTraceSupplier(throwable, supplier); + } + + public static StackTraceSupplier of(String message) { + return new StackTraceSupplier(new IllegalStateException(message), null); + } + @Override public String get() { - return message.get(); + return message != null ? message.get() : throwable.getMessage(); + } + + public StackTraceElement[] getFullStackTrace() { + var innerThrowable = throwable(); + while (innerThrowable.getCause() != null) { + innerThrowable = throwable().getCause(); + } + return innerThrowable.getStackTrace(); + } + + public Throwable throwable() { + return throwable; + } + + @Override + public String toString() { + return "StackTraceSupplier[" + + "throwable=" + throwable + ", " + + "message=" + message + ']'; } }