aboutsummaryrefslogtreecommitdiff
path: root/spark-fabric/src/main/java/me/lucko/spark/fabric/smap
diff options
context:
space:
mode:
Diffstat (limited to 'spark-fabric/src/main/java/me/lucko/spark/fabric/smap')
-rw-r--r--spark-fabric/src/main/java/me/lucko/spark/fabric/smap/MixinUtils.java52
-rw-r--r--spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceDebugCache.java87
-rw-r--r--spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMap.java133
-rw-r--r--spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMapProvider.java53
4 files changed, 325 insertions, 0 deletions
diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/MixinUtils.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/MixinUtils.java
new file mode 100644
index 0000000..ebf2766
--- /dev/null
+++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/MixinUtils.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.fabric.smap;
+
+import org.spongepowered.asm.mixin.transformer.Config;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public enum MixinUtils {
+ ;
+
+ private static final Map<String, Config> MIXIN_CONFIGS;
+
+ static {
+ Map<String, Config> configs;
+ try {
+ Field allConfigsField = Config.class.getDeclaredField("allConfigs");
+ allConfigsField.setAccessible(true);
+
+ //noinspection unchecked
+ configs = (Map<String, Config>) allConfigsField.get(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ configs = new HashMap<>();
+ }
+ MIXIN_CONFIGS = configs;
+ }
+
+ public static Map<String, Config> getMixinConfigs() {
+ return MIXIN_CONFIGS;
+ }
+}
diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceDebugCache.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceDebugCache.java
new file mode 100644
index 0000000..88adae6
--- /dev/null
+++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceDebugCache.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.fabric.smap;
+
+import org.objectweb.asm.tree.ClassNode;
+import org.spongepowered.asm.service.IClassBytecodeProvider;
+import org.spongepowered.asm.service.MixinService;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Caches the lookup of class -> source debug info for classes loaded on the JVM.
+ *
+ * The {@link me.lucko.spark.fabric.plugin.FabricSparkMixinPlugin} also supplements this cache with
+ * extra information as classes are exported.
+ */
+public enum SourceDebugCache {
+ ;
+
+ // class name -> smap
+ private static final Map<String, SmapValue> CACHE = new ConcurrentHashMap<>();
+
+ public static void put(String className, ClassNode node) {
+ if (className == null || node == null) {
+ return;
+ }
+ className = className.replace('/', '.');
+ CACHE.put(className, SmapValue.of(node.sourceDebug));
+ }
+
+ public static String getSourceDebugInfo(String className) {
+ SmapValue cached = CACHE.get(className);
+ if (cached != null) {
+ return cached.value();
+ }
+
+ try {
+ IClassBytecodeProvider provider = MixinService.getService().getBytecodeProvider();
+ ClassNode classNode = provider.getClassNode(className.replace('.', '/'));
+
+ if (classNode != null) {
+ put(className, classNode);
+ return classNode.sourceDebug;
+ }
+
+ } catch (Exception e) {
+ // ignore
+ }
+
+ CACHE.put(className, SmapValue.NULL);
+ return null;
+ }
+
+ private record SmapValue(String value) {
+ static final SmapValue NULL = new SmapValue(null);
+
+ static SmapValue of(String value) {
+ if (value == null) {
+ return NULL;
+ } else {
+ return new SmapValue(value);
+ }
+ }
+
+ }
+
+}
diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMap.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMap.java
new file mode 100644
index 0000000..5105a26
--- /dev/null
+++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMap.java
@@ -0,0 +1,133 @@
+/*
+ * SMAPSourceDebugExtension.java - Parse source debug extensions and
+ * enhance stack traces.
+ *
+ * Copyright (c) 2012 Michael Schierl
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package me.lucko.spark.fabric.smap;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility class to parse "SMAP" (source map) information from loaded Java classes.
+ *
+ * @author <a href="https://stackoverflow.com/a/11299757">Michael Schierl</a>
+ */
+public class SourceMap {
+
+ private final String generatedFileName;
+ private final String firstStratum;
+ private final Map<Integer, FileInfo> fileinfo = new HashMap<>();
+ private final Map<Integer, int[]> reverseLineMapping = new HashMap<>();
+
+ private static final Pattern LINE_INFO_PATTERN = Pattern.compile("([0-9]+)(?:#([0-9]+))?(?:,([0-9]+))?:([0-9]+)(?:,([0-9]+))?");
+
+ public SourceMap(String value) {
+ String[] lines = value.split("\n");
+ if (!lines[0].equals("SMAP") || !lines[3].startsWith("*S ") || !lines[4].equals("*F")) {
+ throw new IllegalArgumentException(value);
+ }
+
+ this.generatedFileName = lines[1];
+ this.firstStratum = lines[3].substring(3);
+
+ int idx = 5;
+ while (!lines[idx].startsWith("*")) {
+ String infoline = lines[idx++];
+ String path = null;
+
+ if (infoline.startsWith("+ ")) {
+ path = lines[idx++];
+ infoline = infoline.substring(2);
+ }
+
+ int pos = infoline.indexOf(" ");
+ int filenum = Integer.parseInt(infoline.substring(0, pos));
+ String name = infoline.substring(pos + 1);
+
+ this.fileinfo.put(filenum, new FileInfo(name, path == null ? name : path));
+ }
+
+ if (lines[idx].equals("*L")) {
+ idx++;
+ int lastLFI = 0;
+
+ while (!lines[idx].startsWith("*")) {
+ Matcher m = LINE_INFO_PATTERN.matcher(lines[idx++]);
+ if (!m.matches()) {
+ throw new IllegalArgumentException(lines[idx - 1]);
+ }
+
+ int inputStartLine = Integer.parseInt(m.group(1));
+ int lineFileID = m.group(2) == null ? lastLFI : Integer.parseInt(m.group(2));
+ int repeatCount = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));
+ int outputStartLine = Integer.parseInt(m.group(4));
+ int outputLineIncrement = m.group(5) == null ? 1 : Integer.parseInt(m.group(5));
+
+ for (int i = 0; i < repeatCount; i++) {
+ int[] inputMapping = new int[] { lineFileID, inputStartLine + i };
+ int baseOL = outputStartLine + i * outputLineIncrement;
+
+ for (int ol = baseOL; ol < baseOL + outputLineIncrement; ol++) {
+ if (!this.reverseLineMapping.containsKey(ol)) {
+ this.reverseLineMapping.put(ol, inputMapping);
+ }
+ }
+ }
+
+ lastLFI = lineFileID;
+ }
+ }
+ }
+
+ public String getGeneratedFileName() {
+ return this.generatedFileName;
+ }
+
+ public String getFirstStratum() {
+ return this.firstStratum;
+ }
+
+ public Map<Integer, FileInfo> getFileInfo() {
+ return this.fileinfo;
+ }
+
+ public Map<Integer, int[]> getReverseLineMapping() {
+ return this.reverseLineMapping;
+ }
+
+ public record FileInfo(String name, String path) { }
+} \ No newline at end of file
diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMapProvider.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMapProvider.java
new file mode 100644
index 0000000..1a4f246
--- /dev/null
+++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/smap/SourceMapProvider.java
@@ -0,0 +1,53 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * Copyright (c) contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.fabric.smap;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SourceMapProvider {
+ private final Map<String, SourceMap> cache = new HashMap<>();
+
+ public @Nullable SourceMap getSourceMap(String className) {
+ if (this.cache.containsKey(className)) {
+ return this.cache.get(className);
+ }
+
+ SourceMap smap = null;
+ try {
+ String value = SourceDebugCache.getSourceDebugInfo(className);
+ if (value != null) {
+ value = value.replaceAll("\r\n?", "\n");
+ if (value.startsWith("SMAP\n")) {
+ smap = new SourceMap(value);
+ }
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+
+ this.cache.put(className, smap);
+ return smap;
+ }
+
+}