backport FernFlowerTask and relevant systems from 0.3
This commit is contained in:
		
							parent
							
								
									3094d70731
								
							
						
					
					
						commit
						b37079c931
					
				
					 16 changed files with 967 additions and 502 deletions
				
			
		|  | @ -11,7 +11,7 @@ targetCompatibility = 1.8 | |||
| 
 | ||||
| group = 'net.fabricmc' | ||||
| archivesBaseName = project.name | ||||
| version = '0.2.1-SNAPSHOT' | ||||
| version = '0.2.2-SNAPSHOT' | ||||
| 
 | ||||
| def build = "local" | ||||
| def ENV = System.getenv() | ||||
|  |  | |||
|  | @ -136,27 +136,8 @@ public class AbstractPlugin implements Plugin<Project> { | |||
| 		configureMaven(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Permit to create a Task instance of the type in the project | ||||
| 	 * | ||||
| 	 * @param name The name of the task | ||||
| 	 * @param type The type of the task that will be used to create an instance | ||||
| 	 * @return The created task object for the project | ||||
| 	 */ | ||||
| 	public <T extends Task> T makeTask(String name, Class<T> type) { | ||||
| 		return makeTask(project, name, type); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Permit to create a Task instance of the type in a project | ||||
| 	 * | ||||
| 	 * @param target The target project | ||||
| 	 * @param name The name of the task | ||||
| 	 * @param type The type of the task that will be used to create an instance | ||||
| 	 * @return The created task object for the specified project | ||||
| 	 */ | ||||
| 	public static <T extends Task> T makeTask(Project target, String name, Class<T> type) { | ||||
| 		return target.getTasks().create(name, type); | ||||
| 	public Project getProject() { | ||||
| 		return project; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
|  |  | |||
|  | @ -24,30 +24,110 @@ | |||
| 
 | ||||
| package net.fabricmc.loom; | ||||
| 
 | ||||
| import net.fabricmc.loom.providers.MappingsProvider; | ||||
| import net.fabricmc.loom.providers.MinecraftLibraryProvider; | ||||
| import net.fabricmc.loom.task.*; | ||||
| import net.fabricmc.loom.task.fernflower.FernFlowerTask; | ||||
| import net.fabricmc.loom.util.LineNumberRemapper; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.tasks.TaskContainer; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| public class LoomGradlePlugin extends AbstractPlugin { | ||||
| 	private static File getMappedByproduct(Project project, String suffix) { | ||||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		MappingsProvider mappingsProvider = extension.getMappingsProvider(); | ||||
| 		File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); | ||||
| 		String path = mappedJar.getAbsolutePath(); | ||||
| 		if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) { | ||||
| 			throw new RuntimeException("Invalid mapped JAR path: " + path); | ||||
| 		} | ||||
| 
 | ||||
| 		return new File(path.substring(0, path.length() - 4) + suffix); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void apply(Project target) { | ||||
| 		super.apply(target); | ||||
| 
 | ||||
| 		makeTask("cleanLoomBinaries", CleanLoomBinaries.class); | ||||
| 		makeTask("cleanLoomMappings", CleanLoomMappings.class); | ||||
| 		TaskContainer tasks = target.getTasks(); | ||||
| 		 | ||||
| 		tasks.register("cleanLoomBinaries", CleanLoomBinaries.class); | ||||
| 		tasks.register("cleanLoomMappings", CleanLoomMappings.class); | ||||
| 
 | ||||
| 		makeTask("remapJar", RemapJar.class); | ||||
| 		tasks.register("remapJar", RemapJar.class); | ||||
| 
 | ||||
| 		makeTask("genSources", GenSourcesTask.class); | ||||
| 		tasks.register("genSources", FernFlowerTask.class, t -> { | ||||
| 			t.getOutputs().upToDateWhen((o) -> false); | ||||
| 		}); | ||||
| 		project.afterEvaluate((p) -> { | ||||
| 			FernFlowerTask task = (FernFlowerTask) p.getTasks().getByName("genSources"); | ||||
| 
 | ||||
| 		makeTask("downloadAssets", DownloadAssetsTask.class); | ||||
| 			Project project = this.getProject(); | ||||
| 			LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 			MinecraftLibraryProvider libraryProvider = extension.getMinecraftProvider().libraryProvider; | ||||
| 			MappingsProvider mappingsProvider = extension.getMappingsProvider(); | ||||
| 			File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); | ||||
| 			File sourcesJar = getMappedByproduct(project, "-sources-tmp.jar"); | ||||
| 			File sourcesFinalJar = getMappedByproduct(project, "-sources.jar"); | ||||
| 			File linemapFile = getMappedByproduct(project, "-sources.lmap"); | ||||
| 
 | ||||
| 		makeTask("genIdeaWorkspace", GenIdeaProjectTask.class).dependsOn("idea", "downloadAssets").setGroup("ide"); | ||||
| 		makeTask("genEclipseRuns", GenEclipseRunsTask.class).dependsOn("downloadAssets").setGroup("ide"); | ||||
| 		makeTask("vscode", GenVsCodeProjectTask.class).dependsOn("downloadAssets").setGroup("ide"); | ||||
| 			task.setInput(mappedJar); | ||||
| 			task.setOutput(sourcesJar); | ||||
| 			task.setLineMapFile(linemapFile); | ||||
| 			task.setLibraries(libraryProvider.getLibraries()); | ||||
| 
 | ||||
| 		makeTask("remapSourcesJar", RemapSourcesJar.class); | ||||
| 			if (sourcesJar.exists()) { | ||||
| 				sourcesJar.delete(); | ||||
| 			} | ||||
| 
 | ||||
| 		makeTask("runClient", RunClientTask.class).dependsOn("buildNeeded", "downloadAssets").setGroup("minecraftMapped"); | ||||
| 		makeTask("runServer", RunServerTask.class).dependsOn("buildNeeded").setGroup("minecraftMapped"); | ||||
| 			task.doLast((tt) -> { | ||||
| 				project.getLogger().lifecycle(":readjusting line numbers"); | ||||
| 				LineNumberRemapper remapper = new LineNumberRemapper(); | ||||
| 				remapper.readMappings(linemapFile); | ||||
| 
 | ||||
| 				try { | ||||
| 					remapper.process(sourcesJar.toPath(), sourcesFinalJar.toPath()); | ||||
| 				} catch (IOException e) { | ||||
| 					throw new RuntimeException(e); | ||||
| 				} | ||||
| 
 | ||||
| 				if (sourcesJar.exists()) { | ||||
| 					sourcesJar.delete(); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.register("downloadAssets", DownloadAssetsTask.class); | ||||
| 
 | ||||
| 		tasks.register("genIdeaWorkspace", GenIdeaProjectTask.class, t -> { | ||||
| 			t.dependsOn("idea", "downloadAssets"); | ||||
| 			t.setGroup("ide"); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.register("genEclipseRuns", GenEclipseRunsTask.class, t -> { | ||||
| 			t.dependsOn("downloadAssets"); | ||||
| 			t.setGroup("ide"); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.register("vscode", GenVsCodeProjectTask.class, t -> { | ||||
| 			t.dependsOn("downloadAssets"); | ||||
| 			t.setGroup("ide"); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.register("remapSourcesJar", RemapSourcesJar.class); | ||||
| 
 | ||||
| 		tasks.register("runClient", RunClientTask.class, t -> { | ||||
| 			t.dependsOn("buildNeeded", "downloadAssets"); | ||||
| 			t.setGroup("minecraftMapped"); | ||||
| 		}); | ||||
| 
 | ||||
| 		tasks.register("runServer", RunServerTask.class, t -> { | ||||
| 			t.dependsOn("buildNeeded"); | ||||
| 			t.setGroup("minecraftMapped"); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,53 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| 
 | ||||
| import org.gradle.api.Action; | ||||
| import org.gradle.api.Task; | ||||
| import org.gradle.api.artifacts.ConfigurationContainer; | ||||
| import org.gradle.api.artifacts.dsl.DependencyHandler; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.process.ExecResult; | ||||
| import org.gradle.process.JavaExecSpec; | ||||
| 
 | ||||
| /** | ||||
|  * Simple trait like interface for a Task that wishes to execute a java process | ||||
|  * with the classpath of the gradle plugin plus groovy. | ||||
|  * | ||||
|  * Created by covers1624 on 11/02/19. | ||||
|  */ | ||||
| public interface ForkingJavaExecTask extends Task { | ||||
| 
 | ||||
|     default ExecResult javaexec(Action<? super JavaExecSpec> action) { | ||||
|         ConfigurationContainer configurations = getProject().getBuildscript().getConfigurations(); | ||||
|         DependencyHandler handler = getProject().getDependencies(); | ||||
|         FileCollection classpath = configurations.getByName("classpath")// | ||||
|                 .plus(configurations.detachedConfiguration(handler.localGroovy())); | ||||
|         return getProject().javaexec(spec -> { | ||||
|             spec.classpath(classpath); | ||||
|             action.execute(spec); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | @ -1,116 +0,0 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| 
 | ||||
| public class GenSourcesCfrTask extends DefaultLoomTask { | ||||
| 	/* | ||||
| 	@TaskAction | ||||
| 	public void genSources() throws IOException { | ||||
| 		Project project = this.getProject(); | ||||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		MappingsProvider mappingsProvider = extension.getMappingsProvider(); | ||||
| 		File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); | ||||
| 		File sourcesJar = getSourcesJar(project); | ||||
| 
 | ||||
| 		Manifest manifest = new Manifest(); | ||||
| 		manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); | ||||
| 		Set<String> addedDirectories = new HashSet<>(); | ||||
| 
 | ||||
| 		try (FileOutputStream fos = new FileOutputStream(sourcesJar); | ||||
| 			JarOutputStream jos = new JarOutputStream(fos, manifest)) { | ||||
| 
 | ||||
| 			project.getLogger().lifecycle(":generating sources JAR"); | ||||
| 			CfrDriver driver = new CfrDriver.Builder() | ||||
| 					.withOptions(ImmutableMap.of("renameillegalidents","true")) | ||||
| 					.withOutputSink(new OutputSinkFactory() { | ||||
| 						@Override | ||||
| 						public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass> collection) { | ||||
| 							switch (sinkType) { | ||||
| 								case PROGRESS: | ||||
| 									return Collections.singletonList(SinkClass.STRING); | ||||
| 								case JAVA: | ||||
| 									return Collections.singletonList(SinkClass.DECOMPILED); | ||||
| 								default: | ||||
| 									return Collections.emptyList(); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						@Override | ||||
| 						public <T> Sink<T> getSink(SinkType sinkType, SinkClass sinkClass) { | ||||
| 							switch (sinkType) { | ||||
| 								case PROGRESS: | ||||
| 									return (t) -> getLogger().debug((String) t); | ||||
| 								case JAVA: | ||||
| 									//noinspection unchecked | ||||
| 									return (Sink<T>) new Sink<SinkReturns.Decompiled>() { | ||||
| 										@Override | ||||
| 										public void write(SinkReturns.Decompiled decompiled) { | ||||
| 											String filename = decompiled.getPackageName().replace('.', '/'); | ||||
| 											if (!filename.isEmpty()) filename += "/"; | ||||
| 											filename += decompiled.getClassName() + ".java"; | ||||
| 
 | ||||
| 											String[] path = filename.split("/"); | ||||
| 											String pathPart = ""; | ||||
| 											for (int i = 0; i < path.length - 1; i++) { | ||||
| 												pathPart += path[i] + "/"; | ||||
| 												if (addedDirectories.add(pathPart)) { | ||||
| 													JarEntry entry = new JarEntry(pathPart); | ||||
| 													entry.setTime(new Date().getTime()); | ||||
| 
 | ||||
| 													try { | ||||
| 														jos.putNextEntry(entry); | ||||
| 														jos.closeEntry(); | ||||
| 													} catch (IOException e) { | ||||
| 														throw new RuntimeException(e); | ||||
| 													} | ||||
| 												} | ||||
| 											} | ||||
| 
 | ||||
| 											byte[] data = decompiled.getJava().getBytes(Charsets.UTF_8); | ||||
| 											JarEntry entry = new JarEntry(filename); | ||||
| 											entry.setTime(new Date().getTime()); | ||||
| 											entry.setSize(data.length); | ||||
| 
 | ||||
| 											try { | ||||
| 												jos.putNextEntry(entry); | ||||
| 												jos.write(data); | ||||
| 												jos.closeEntry(); | ||||
| 											} catch (IOException e) { | ||||
| 												throw new RuntimeException(e); | ||||
| 											} | ||||
| 										} | ||||
| 									}; | ||||
| 								default: | ||||
| 									return (t) -> {}; | ||||
| 							} | ||||
| 						} | ||||
| 					}) | ||||
| 					.build(); | ||||
| 
 | ||||
| 			driver.analyse(Collections.singletonList(mappedJar.getAbsolutePath())); | ||||
| 		} | ||||
| 	} | ||||
| 	*/ | ||||
| } | ||||
|  | @ -1,143 +0,0 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| 
 | ||||
| import com.google.common.io.ByteStreams; | ||||
| import com.google.common.io.Files; | ||||
| import net.fabricmc.loom.LoomGradleExtension; | ||||
| import net.fabricmc.loom.providers.MappingsProvider; | ||||
| import net.fabricmc.loom.providers.MinecraftLibraryProvider; | ||||
| import org.gradle.api.Project; | ||||
| import org.gradle.api.tasks.TaskAction; | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; | ||||
| import org.objectweb.asm.ClassReader; | ||||
| import org.objectweb.asm.ClassWriter; | ||||
| import org.objectweb.asm.Opcodes; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
| import java.util.jar.*; | ||||
| 
 | ||||
| public class GenSourcesTask extends DefaultLoomTask { | ||||
| 	public static File getSourcesJar(Project project) { | ||||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		MappingsProvider mappingsProvider = extension.getMappingsProvider(); | ||||
| 		File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); | ||||
| 		String path = mappedJar.getAbsolutePath(); | ||||
| 		if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) { | ||||
| 			throw new RuntimeException("Invalid mapped JAR path: " + path); | ||||
| 		} | ||||
| 
 | ||||
| 		return new File(path.substring(0, path.length() - 4) + "-sources.jar"); | ||||
| 	} | ||||
| 
 | ||||
| 	@TaskAction | ||||
| 	public void genSources() throws IOException { | ||||
| 		Project project = this.getProject(); | ||||
| 		LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); | ||||
| 		MinecraftLibraryProvider libraryProvider = extension.getMinecraftProvider().libraryProvider; | ||||
| 		MappingsProvider mappingsProvider = extension.getMappingsProvider(); | ||||
| 		File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); | ||||
| 		File sourcesJar = getSourcesJar(project); | ||||
| 
 | ||||
| 		Manifest manifest = new Manifest(); | ||||
| 		manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); | ||||
| 
 | ||||
| 		project.getLogger().lifecycle(":preparing sources JAR"); | ||||
| 		Map<String, Object> options = new HashMap<>(); | ||||
| 		options.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); | ||||
| 		options.put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1"); | ||||
| 
 | ||||
| 		LoomFernflowerDecompiler decompiler = new LoomFernflowerDecompiler(sourcesJar.getParentFile(), sourcesJar.getName(), options, new LoomFernflowerLogger()); | ||||
| 		decompiler.addSource(mappedJar); | ||||
| 		for (File lib : libraryProvider.getLibraries()) { | ||||
| 			try { | ||||
| 				decompiler.addLibrary(lib); | ||||
| 			} catch (Exception e) { | ||||
| 				// pass | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		project.getLogger().lifecycle(":generating sources JAR"); | ||||
| 		decompiler.decompileContext(); | ||||
| 
 | ||||
| 		Map<String, int[]> mapNumbers = decompiler.getDifferingMappings(); | ||||
| 		if (!mapNumbers.isEmpty()) { | ||||
| 			project.getLogger().lifecycle(":readjusting line numbers"); | ||||
| 
 | ||||
| 			File tmpJar = new File(mappedJar.getAbsolutePath() + ".tmp"); | ||||
| 			Files.move(mappedJar, tmpJar); | ||||
| 			try ( | ||||
| 					FileInputStream fis = new FileInputStream(tmpJar); | ||||
| 					JarInputStream jis = new JarInputStream(fis); | ||||
| 					FileOutputStream fos = new FileOutputStream(mappedJar); | ||||
| 					JarOutputStream jos = new JarOutputStream(fos) | ||||
| 					) { | ||||
| 				JarEntry entry; | ||||
| 
 | ||||
| 				while ((entry = jis.getNextJarEntry()) != null) { | ||||
| 					JarEntry outputEntry = new JarEntry(entry.getName()); | ||||
| 					outputEntry.setTime(entry.getTime()); | ||||
| 					outputEntry.setCreationTime(entry.getCreationTime()); | ||||
| 					outputEntry.setLastAccessTime(entry.getLastAccessTime()); | ||||
| 					outputEntry.setLastModifiedTime(entry.getLastModifiedTime()); | ||||
| 
 | ||||
| 					if (!entry.getName().endsWith(".class")) { | ||||
| 						jos.putNextEntry(outputEntry); | ||||
| 						ByteStreams.copy(jis, jos); | ||||
| 						jos.closeEntry(); | ||||
| 					} else { | ||||
| 						String idx = entry.getName().substring(0, entry.getName().length() - 6); | ||||
| 						int dollarPos = idx.indexOf('$'); | ||||
| 						if (dollarPos >= 0) { | ||||
| 							idx = idx.substring(0, dollarPos); | ||||
| 						} | ||||
| 
 | ||||
| 						byte[] data = ByteStreams.toByteArray(jis); | ||||
| 						if (mapNumbers.containsKey(idx)) { | ||||
| 							ClassReader reader = new ClassReader(data); | ||||
| 							ClassWriter writer = new ClassWriter(0); | ||||
| 
 | ||||
| 							reader.accept(new LineNumberAdjustmentVisitor(Opcodes.ASM7, writer, mapNumbers.get(idx)), 0); | ||||
| 							data = writer.toByteArray(); | ||||
| 						} | ||||
| 
 | ||||
| 						jos.putNextEntry(outputEntry); | ||||
| 						jos.write(data); | ||||
| 						jos.closeEntry(); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			//noinspection ResultOfMethodCallIgnored | ||||
| 			tmpJar.delete(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -1,88 +0,0 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| 
 | ||||
| import org.objectweb.asm.ClassVisitor; | ||||
| import org.objectweb.asm.Label; | ||||
| import org.objectweb.asm.MethodVisitor; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public class LineNumberAdjustmentVisitor extends ClassVisitor { | ||||
| 	public class Method extends MethodVisitor { | ||||
| 		public Method(int api, MethodVisitor methodVisitor) { | ||||
| 			super(api, methodVisitor); | ||||
| 		} | ||||
| 
 | ||||
| 		@Override | ||||
| 		public void visitLineNumber(final int line, final Label start) { | ||||
| 			int tLine = line; | ||||
| 			if (tLine <= 0) { | ||||
| 				super.visitLineNumber(1, start); | ||||
| 			} else if (tLine >= maxLine) { | ||||
| 				super.visitLineNumber(maxLineDst, start); | ||||
| 			} else { | ||||
| 				Integer matchedLine = null; | ||||
| 
 | ||||
| 				while (tLine <= maxLine && ((matchedLine = lineNumberMap.get(tLine)) == null)) { | ||||
| 					tLine++; | ||||
| 				} | ||||
| 
 | ||||
| 				super.visitLineNumber(matchedLine != null ? matchedLine : maxLineDst, start); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private final Map<Integer, Integer> lineNumberMap; | ||||
| 	private int maxLine, maxLineDst; | ||||
| 
 | ||||
| 	public LineNumberAdjustmentVisitor(int api, ClassVisitor classVisitor, int[] mapping) { | ||||
| 		super(api, classVisitor); | ||||
| 
 | ||||
| 		lineNumberMap = new HashMap<>(); | ||||
| 		maxLine = 0; | ||||
| 
 | ||||
| 		for (int i = 0; i < mapping.length; i += 2) { | ||||
| 			lineNumberMap.put(mapping[i], mapping[i+1]); | ||||
| 			if (mapping[i] > maxLine) { | ||||
| 				maxLine = mapping[i]; | ||||
| 			} | ||||
| 			if (mapping[i+1] > maxLineDst) { | ||||
| 				maxLineDst = mapping[i+1]; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public MethodVisitor visitMethod( | ||||
| 			final int access, | ||||
| 			final String name, | ||||
| 			final String descriptor, | ||||
| 			final String signature, | ||||
| 			final String[] exceptions) { | ||||
| 		return new Method(api, super.visitMethod(access, name, descriptor, signature, exceptions)); | ||||
| 	} | ||||
| } | ||||
|  | @ -1,107 +0,0 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| 
 | ||||
| import net.fabricmc.fernflower.api.IFabricResultSaver; | ||||
| import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler; | ||||
| import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; | ||||
| import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||||
| import org.jetbrains.java.decompiler.main.extern.IResultSaver; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
| import java.util.jar.JarEntry; | ||||
| import java.util.jar.JarOutputStream; | ||||
| import java.util.jar.Manifest; | ||||
| 
 | ||||
| public class LoomFernflowerDecompiler extends ConsoleDecompiler implements IFabricResultSaver { | ||||
| 	private final Map<String, int[]> differingMappings = new HashMap<>(); | ||||
| 	private final String jarName; | ||||
| 
 | ||||
| 	public LoomFernflowerDecompiler(File destination, String jarName, Map<String, Object> options, IFernflowerLogger logger) { | ||||
| 		super(destination, options, logger); | ||||
| 		this.jarName = jarName; | ||||
| 	} | ||||
| 
 | ||||
| 	public Map<String, int[]> getDifferingMappings() { | ||||
| 		return differingMappings; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void saveFolder(String s) { | ||||
| 		super.saveFolder(s); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void copyFile(String s, String s1, String s2) { | ||||
| 		throw new RuntimeException("TODO copyFile " + s + " " + s1 + " " + s2); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void saveClassFile(String s, String s1, String s2, String s3, int[] ints) { | ||||
| 		throw new RuntimeException("TODO saveClassFile " + s + " " + s1 + " " + s2 + " " + s3); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void createArchive(String s, String s1, Manifest manifest) { | ||||
| 		super.createArchive(s, jarName, manifest); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void saveDirEntry(String s, String s1, String s2) { | ||||
| 		super.saveDirEntry(s, jarName, s2); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void copyEntry(String s, String s1, String s2, String s3) { | ||||
| 		if (s3.endsWith(".java")) { | ||||
| 			super.copyEntry(s, s1, jarName, s3); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void saveClassEntry(String s, String s1, String s2, String s3, String s4) { | ||||
| 		if (s2 != null) { | ||||
| 			System.err.println("Warning: No line mapping provided for " + s1 + " : " + s2 + "!"); | ||||
| 		} | ||||
| 		super.saveClassEntry(s, s1, s2, s3, s4); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void saveClassEntry(String s, String s1, String s2, String s3, String s4, int[] mapping) { | ||||
| 		if (s2 != null && mapping != null) { | ||||
| 			differingMappings.put(s2, mapping); | ||||
| 		} | ||||
| 
 | ||||
| 		super.saveClassEntry(s, jarName, s2, s3, s4); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void closeArchive(String s, String s1) { | ||||
| 		super.closeArchive(s, jarName); | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,151 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import net.fabricmc.loom.task.DefaultLoomTask; | ||||
| import net.fabricmc.loom.task.ForkingJavaExecTask; | ||||
| import net.fabricmc.loom.util.ConsumingOutputStream; | ||||
| import org.gradle.api.file.FileCollection; | ||||
| import org.gradle.api.internal.project.ProjectInternal; | ||||
| import org.gradle.api.logging.LogLevel; | ||||
| import org.gradle.api.tasks.Input; | ||||
| import org.gradle.api.tasks.OutputFile; | ||||
| import org.gradle.api.tasks.TaskAction; | ||||
| import org.gradle.internal.logging.progress.ProgressLogger; | ||||
| import org.gradle.internal.logging.progress.ProgressLoggerFactory; | ||||
| import org.gradle.internal.service.ServiceRegistry; | ||||
| import org.gradle.process.ExecResult; | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.*; | ||||
| import java.util.function.Supplier; | ||||
| 
 | ||||
| import static java.text.MessageFormat.format; | ||||
| 
 | ||||
| /** | ||||
|  * Created by covers1624 on 9/02/19. | ||||
|  */ | ||||
| public class FernFlowerTask extends DefaultLoomTask implements ForkingJavaExecTask { | ||||
| 
 | ||||
|     private boolean noFork; | ||||
|     private Object input; | ||||
|     private Object output; | ||||
|     private Object lineMapFile; | ||||
|     private Object libraries; | ||||
|     private int numThreads = Runtime.getRuntime().availableProcessors(); | ||||
| 
 | ||||
|     @TaskAction | ||||
|     public void doTask() throws Throwable { | ||||
|         Map<String, Object> options = new HashMap<>(); | ||||
|         options.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); | ||||
|         options.put(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1"); | ||||
|         options.put(IFernflowerPreferences.LOG_LEVEL, "trace"); | ||||
|         getLogging().captureStandardOutput(LogLevel.LIFECYCLE); | ||||
| 
 | ||||
|         List<String> args = new ArrayList<>(); | ||||
| 
 | ||||
|         options.forEach((k, v) -> args.add(format("-{0}={1}", k, v))); | ||||
|         args.add(getInput().getAbsolutePath()); | ||||
|         args.add("-o=" + getOutput().getAbsolutePath()); | ||||
|         args.add("-l=" + getLineMapFile().getAbsolutePath()); | ||||
|         args.add("-t=" + getNumThreads()); | ||||
| 
 | ||||
|         //TODO, Decompiler breaks on jemalloc, J9 module-info.class? | ||||
|         getLibraries().forEach(f -> args.add("-e=" + f.getAbsolutePath())); | ||||
| 
 | ||||
|         ServiceRegistry registry = ((ProjectInternal) getProject()).getServices(); | ||||
|         ProgressLoggerFactory factory = registry.get(ProgressLoggerFactory.class); | ||||
|         ProgressLogger progressGroup = factory.newOperation(getClass()).setDescription("Decompile"); | ||||
|         Supplier<ProgressLogger> loggerFactory = () -> { | ||||
|             ProgressLogger pl = factory.newOperation(getClass(), progressGroup); | ||||
|             pl.setDescription("decompile worker"); | ||||
|             pl.started(); | ||||
|             return pl; | ||||
|         }; | ||||
|         Stack<ProgressLogger> freeLoggers = new Stack<>(); | ||||
|         Map<String, ProgressLogger> inUseLoggers = new HashMap<>(); | ||||
| 
 | ||||
|         progressGroup.started(); | ||||
|         ExecResult result = javaexec(spec -> { | ||||
|             spec.setMain(ForkedFFExecutor.class.getName()); | ||||
|             spec.jvmArgs("-Xms200m", "-Xmx3G"); | ||||
|             spec.setArgs(args); | ||||
|             spec.setErrorOutput(System.err); | ||||
|             spec.setStandardOutput(new ConsumingOutputStream(line -> { | ||||
|                 if (line.startsWith("Listening for transport")) { | ||||
|                     System.out.println(line); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 int sepIdx = line.indexOf("::"); | ||||
|                 String id = line.substring(0, sepIdx).trim(); | ||||
|                 String data = line.substring(sepIdx + 2).trim(); | ||||
| 
 | ||||
|                 ProgressLogger logger = inUseLoggers.get(id); | ||||
| 
 | ||||
|                 String[] segs = data.split(" "); | ||||
|                 if (segs[0].equals("waiting")) { | ||||
|                     if (logger != null) { | ||||
|                         logger.progress("Idle.."); | ||||
|                         inUseLoggers.remove(id); | ||||
|                         freeLoggers.push(logger); | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (logger == null) { | ||||
|                         if (!freeLoggers.isEmpty()) { | ||||
|                             logger = freeLoggers.pop(); | ||||
|                         } else { | ||||
|                             logger = loggerFactory.get(); | ||||
|                         } | ||||
|                         inUseLoggers.put(id, logger); | ||||
|                     } | ||||
|                     logger.progress(data); | ||||
|                 } | ||||
|             })); | ||||
|         }); | ||||
|         inUseLoggers.values().forEach(ProgressLogger::completed); | ||||
|         freeLoggers.forEach(ProgressLogger::completed); | ||||
|         progressGroup.completed(); | ||||
| 
 | ||||
|         result.rethrowFailure(); | ||||
|         result.assertNormalExitValue(); | ||||
|     } | ||||
| 
 | ||||
|     //@formatter:off | ||||
|     @Input public File getInput() { return getProject().file(input); } | ||||
|     @OutputFile public File getOutput() { return getProject().file(output); } | ||||
|     @OutputFile public File getLineMapFile() { return getProject().file(lineMapFile); } | ||||
|     @Input public FileCollection getLibraries() { return getProject().files(libraries); } | ||||
|     @Input public int getNumThreads() { return numThreads; } | ||||
|     public boolean isNoFork() { return noFork; } | ||||
|     public void setInput(Object input) { this.input = input; } | ||||
|     public void setOutput(Object output) { this.output = output; } | ||||
|     public void setLineMapFile(Object lineMapFile) { this.lineMapFile = lineMapFile; } | ||||
|     public void setLibraries(Object libraries) { this.libraries = libraries; } | ||||
|     public void setNoFork(boolean noFork) { this.noFork = noFork; } | ||||
|     public void setNumThreads(int numThreads) { this.numThreads = numThreads; } | ||||
|     //@formatter:on | ||||
| } | ||||
|  | @ -0,0 +1,49 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import org.jetbrains.java.decompiler.util.InterpreterUtil; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.zip.ZipEntry; | ||||
| import java.util.zip.ZipFile; | ||||
| 
 | ||||
| public class FernFlowerUtils { | ||||
| 	public static byte[] getBytecode(String externalPath, String internalPath) throws IOException { | ||||
| 	    File file = new File(externalPath); | ||||
| 	    if (internalPath == null) { | ||||
| 	        return InterpreterUtil.getBytes(file); | ||||
| 	    } else { | ||||
| 	        try (ZipFile archive = new ZipFile(file)) { | ||||
| 	            ZipEntry entry = archive.getEntry(internalPath); | ||||
| 	            if (entry == null) { | ||||
| 	                throw new IOException("Entry not found: " + internalPath); | ||||
| 	            } | ||||
| 	            return InterpreterUtil.getBytes(archive, entry); | ||||
| 	        } | ||||
| 	    } | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,105 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import org.jetbrains.java.decompiler.main.Fernflower; | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||||
| import org.jetbrains.java.decompiler.main.extern.IResultSaver; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
| 
 | ||||
| /** | ||||
|  * Entry point for Forked FernFlower task. | ||||
|  * Takes one parameter, a single file, each line is treated as command line input. | ||||
|  * Forces one input file. | ||||
|  * Forces one output file using '-o=/path/to/output' | ||||
|  * | ||||
|  * Created by covers1624 on 11/02/19. | ||||
|  */ | ||||
| public class ForkedFFExecutor { | ||||
| 
 | ||||
|     public static void main(String[] args) throws IOException { | ||||
|         Map<String, Object> options = new HashMap<>(); | ||||
|         File input = null; | ||||
|         File output = null; | ||||
|         File lineMap = null; | ||||
|         List<File> libraries = new ArrayList<>(); | ||||
|         int numThreads = 0; | ||||
| 
 | ||||
|         boolean isOption = true; | ||||
|         for (String arg : args) { | ||||
|             if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') { | ||||
|                 String value = arg.substring(5); | ||||
|                 if ("true".equalsIgnoreCase(value)) { | ||||
|                     value = "1"; | ||||
|                 } else if ("false".equalsIgnoreCase(value)) { | ||||
|                     value = "0"; | ||||
|                 } | ||||
| 
 | ||||
|                 options.put(arg.substring(1, 4), value); | ||||
|             } else { | ||||
|                 isOption = false; | ||||
|                 if (arg.startsWith("-e=")) { | ||||
|                     libraries.add(new File(arg.substring(3))); | ||||
|                 } else if (arg.startsWith("-o=")) { | ||||
|                     if (output != null) { | ||||
|                         throw new RuntimeException("Unable to set more than one output."); | ||||
|                     } | ||||
|                     output = new File(arg.substring(3)); | ||||
|                 } else if (arg.startsWith("-l=")) { | ||||
|                     if (lineMap != null) { | ||||
|                         throw new RuntimeException("Unable to set more than one lineMap file."); | ||||
|                     } | ||||
|                     lineMap = new File(arg.substring(3)); | ||||
|                 } else if (arg.startsWith("-t=")) { | ||||
|                     numThreads = Integer.parseInt(arg.substring(3)); | ||||
|                 } else { | ||||
|                     if (input != null) { | ||||
|                         throw new RuntimeException("Unable to set more than one input."); | ||||
|                     } | ||||
|                     input = new File(arg); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Objects.requireNonNull(input, "Input not set."); | ||||
|         Objects.requireNonNull(output, "Output not set."); | ||||
| 
 | ||||
|         runFF(options, libraries, input, output, lineMap); | ||||
|     } | ||||
| 
 | ||||
|     public static void runFF(Map<String, Object> options, List<File> libraries, File input, File output, File lineMap) { | ||||
|         IResultSaver saver = new ThreadSafeResultSaver(() -> output, () -> lineMap); | ||||
|         IFernflowerLogger logger = new ThreadIDFFLogger(); | ||||
|         Fernflower ff = new Fernflower(FernFlowerUtils::getBytecode, saver, options, logger); | ||||
|         for (File library : libraries) { | ||||
|             ff.addLibrary(library); | ||||
|         } | ||||
|         ff.addSource(input); | ||||
|         ff.decompileContext(); | ||||
|     } | ||||
| } | ||||
|  | @ -22,23 +22,23 @@ | |||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task; | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||||
| 
 | ||||
| public class LoomFernflowerLogger extends IFernflowerLogger { | ||||
| 	@Override | ||||
| 	public void writeMessage(String s, Severity severity) { | ||||
| 		if (severity == Severity.WARN || severity == Severity.ERROR) { | ||||
| 			System.err.println(s); | ||||
| 		} | ||||
| 	} | ||||
| /** | ||||
|  * Literally does nothing. | ||||
|  * Created by covers1624 on 11/02/19. | ||||
|  */ | ||||
| public class NoopFFLogger extends IFernflowerLogger { | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void writeMessage(String s, Severity severity, Throwable throwable) { | ||||
| 		if (severity == Severity.WARN || severity == Severity.ERROR) { | ||||
| 			System.err.println(s); | ||||
| 			throwable.printStackTrace(System.err); | ||||
| 		} | ||||
| 	} | ||||
|     @Override | ||||
|     public void writeMessage(String message, Severity severity) { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeMessage(String message, Severity severity, Throwable t) { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,131 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||||
| 
 | ||||
| import java.io.PrintStream; | ||||
| import java.text.MessageFormat; | ||||
| import java.util.Stack; | ||||
| 
 | ||||
| /** | ||||
|  * This logger simply prints what each thread is doing | ||||
|  * to the console in a machine parsable way. | ||||
|  * | ||||
|  * Created by covers1624 on 11/02/19. | ||||
|  */ | ||||
| public class ThreadIDFFLogger extends IFernflowerLogger { | ||||
| 
 | ||||
|     public final PrintStream stdOut; | ||||
|     public final PrintStream stdErr; | ||||
| 
 | ||||
|     private ThreadLocal<Stack<String>> workingClass = ThreadLocal.withInitial(Stack::new); | ||||
|     private ThreadLocal<Stack<String>> line = ThreadLocal.withInitial(Stack::new); | ||||
| 
 | ||||
|     public ThreadIDFFLogger() { | ||||
|         this(System.err, System.out); | ||||
|     } | ||||
| 
 | ||||
|     public ThreadIDFFLogger(PrintStream stdOut, PrintStream stdErr) { | ||||
|         this.stdOut = stdOut; | ||||
|         this.stdErr = stdErr; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeMessage(String message, Severity severity) { | ||||
|         System.err.println(message); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeMessage(String message, Severity severity, Throwable t) { | ||||
|         System.err.println(message); | ||||
|         t.printStackTrace(System.err); | ||||
|     } | ||||
| 
 | ||||
|     private void print() { | ||||
|         Thread thread = Thread.currentThread(); | ||||
|         long id = thread.getId(); | ||||
|         if (line.get().isEmpty()) { | ||||
|             System.out.println(MessageFormat.format("{0} :: waiting", id)); | ||||
|             return; | ||||
|         } | ||||
|         String line = this.line.get().peek(); | ||||
|         System.out.println(MessageFormat.format("{0} :: {1}", id, line).trim()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void startReadingClass(String className) { | ||||
|         workingClass.get().push(className); | ||||
|         line.get().push("Decompiling " + className); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void startClass(String className) { | ||||
|         workingClass.get().push(className); | ||||
|         line.get().push("Decompiling " + className); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void startMethod(String methodName) { | ||||
|         String className = workingClass.get().peek(); | ||||
|         line.get().push("Decompiling " + className + "." + methodName.substring(0, methodName.indexOf(" "))); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void endMethod() { | ||||
|         line.get().pop(); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void endClass() { | ||||
|         line.get().pop(); | ||||
|         workingClass.get().pop(); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void startWriteClass(String className) { | ||||
|         line.get().push("Writing " + className); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void endWriteClass() { | ||||
|         line.get().pop(); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void endReadingClass() { | ||||
|         line.get().pop(); | ||||
|         workingClass.get().pop(); | ||||
|         print(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,150 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.task.fernflower; | ||||
| 
 | ||||
| import net.fabricmc.fernflower.api.IFabricResultSaver; | ||||
| import org.jetbrains.java.decompiler.main.DecompilerContext; | ||||
| import org.jetbrains.java.decompiler.main.extern.IResultSaver; | ||||
| 
 | ||||
| import java.io.*; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ExecutionException; | ||||
| import java.util.concurrent.ExecutorService; | ||||
| import java.util.concurrent.Executors; | ||||
| import java.util.concurrent.Future; | ||||
| import java.util.function.Supplier; | ||||
| import java.util.jar.JarOutputStream; | ||||
| import java.util.jar.Manifest; | ||||
| import java.util.zip.ZipEntry; | ||||
| import java.util.zip.ZipOutputStream; | ||||
| 
 | ||||
| /** | ||||
|  * Created by covers1624 on 18/02/19. | ||||
|  */ | ||||
| public class ThreadSafeResultSaver implements IResultSaver, IFabricResultSaver { | ||||
|     private final Supplier<File> output; | ||||
|     private final Supplier<File> lineMapFile; | ||||
| 
 | ||||
|     public Map<String, ZipOutputStream> outputStreams = new HashMap<>(); | ||||
|     public Map<String, ExecutorService> saveExecutors = new HashMap<>(); | ||||
|     public PrintWriter lineMapWriter; | ||||
| 
 | ||||
|     public ThreadSafeResultSaver(Supplier<File> output, Supplier<File> lineMapFile) { | ||||
|         this.output = output; | ||||
|         this.lineMapFile = lineMapFile; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void createArchive(String path, String archiveName, Manifest manifest) { | ||||
|         String key = path + "/" + archiveName; | ||||
|         File file = output.get(); | ||||
|         try { | ||||
|             FileOutputStream fos = new FileOutputStream(file); | ||||
|             ZipOutputStream zos = manifest == null ? new ZipOutputStream(fos) : new JarOutputStream(fos, manifest); | ||||
|             outputStreams.put(key, zos); | ||||
|             saveExecutors.put(key, Executors.newSingleThreadExecutor()); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("Unable to create archive: " + file, e); | ||||
|         } | ||||
|         if (lineMapFile.get() != null) { | ||||
|             try { | ||||
|                 lineMapWriter = new PrintWriter(new FileWriter(lineMapFile.get())); | ||||
|             } catch (IOException e) { | ||||
|                 throw new RuntimeException("Unable to create LineMap file.", e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { | ||||
|         this.saveClassEntry(path, archiveName, qualifiedName, entryName, content, null); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content, int[] mapping) { | ||||
|         String key = path + "/" + archiveName; | ||||
|         ExecutorService executor = saveExecutors.get(key); | ||||
|         executor.submit(() -> { | ||||
|             ZipOutputStream zos = outputStreams.get(key); | ||||
|             try { | ||||
|                 zos.putNextEntry(new ZipEntry(entryName)); | ||||
|                 if (content != null) { | ||||
|                     zos.write(content.getBytes(StandardCharsets.UTF_8)); | ||||
|                 } | ||||
|             } catch (IOException e) { | ||||
|                 DecompilerContext.getLogger().writeMessage("Cannot write entry " + entryName, e); | ||||
|             } | ||||
|             if (mapping != null && lineMapWriter != null) { | ||||
|                 int maxLine = 0; | ||||
|                 int maxLineDist = 0; | ||||
|                 // 2 loops here is meh, perhaps merge with a buffer? | ||||
|                 for (int i = 0; i < mapping.length; i += 2) { | ||||
|                     maxLine = Math.max(maxLine, mapping[i]); | ||||
|                     maxLineDist = Math.max(maxLineDist, mapping[i + 1]); | ||||
|                 } | ||||
|                 lineMapWriter.println(qualifiedName + "\t" + maxLine + "\t" + maxLineDist); | ||||
|                 for (int i = 0; i < mapping.length; i += 2) { | ||||
|                     lineMapWriter.println("\t" + mapping[i] + "\t" + mapping[i + 1]); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void closeArchive(String path, String archiveName) { | ||||
|         String key = path + "/" + archiveName; | ||||
|         ExecutorService executor = saveExecutors.get(key); | ||||
|         Future<?> closeFuture = executor.submit(() -> { | ||||
|             ZipOutputStream zos = outputStreams.get(key); | ||||
|             try { | ||||
|                 zos.close(); | ||||
|             } catch (IOException e) { | ||||
|                 throw new RuntimeException("Unable to close zip. " + key, e); | ||||
|             } | ||||
|         }); | ||||
|         executor.shutdown(); | ||||
|         try { | ||||
|             closeFuture.get(); | ||||
|         } catch (InterruptedException | ExecutionException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|         outputStreams.remove(key); | ||||
|         saveExecutors.remove(key); | ||||
|         if (lineMapWriter != null) { | ||||
|             lineMapWriter.flush(); | ||||
|             lineMapWriter.close(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //@formatter:off | ||||
|     @Override public void saveFolder(String path) { } | ||||
|     @Override public void copyFile(String source, String path, String entryName) { } | ||||
|     @Override public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { } | ||||
|     @Override public void saveDirEntry(String path, String archiveName, String entryName) { } | ||||
|     @Override public void copyEntry(String source, String path, String archiveName, String entry) { } | ||||
|     //@formatter:on | ||||
| } | ||||
|  | @ -0,0 +1,64 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.util; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.io.OutputStream; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| /** | ||||
|  * Created by covers1624 on 20/12/18. | ||||
|  */ | ||||
| public class ConsumingOutputStream extends OutputStream { | ||||
| 
 | ||||
|     private final Consumer<String> consumer; | ||||
| 
 | ||||
|     private StringBuilder buffer = new StringBuilder(); | ||||
| 
 | ||||
|     public ConsumingOutputStream(Consumer<String> consumer) { | ||||
|         this.consumer = consumer; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void write(int b) throws IOException { | ||||
|         char ch = (char) (b & 0xFF); | ||||
|         buffer.append(ch); | ||||
|         if (ch == '\n' || ch == '\r') { | ||||
|             flush(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void flush() throws IOException { | ||||
|         String str = buffer.toString(); | ||||
|         if (str.endsWith("\r") || str.endsWith("\n")) { | ||||
|             str = str.trim(); | ||||
|             if (!str.isEmpty()) { | ||||
|                 consumer.accept(str); | ||||
|             } | ||||
|             buffer = new StringBuilder(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										155
									
								
								src/main/java/net/fabricmc/loom/util/LineNumberRemapper.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/main/java/net/fabricmc/loom/util/LineNumberRemapper.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | |||
| /* | ||||
|  * This file is part of fabric-loom, licensed under the MIT License (MIT). | ||||
|  * | ||||
|  * Copyright (c) 2016, 2017, 2018 FabricMC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package net.fabricmc.loom.util; | ||||
| 
 | ||||
| import org.objectweb.asm.*; | ||||
| 
 | ||||
| import java.io.*; | ||||
| import java.nio.file.*; | ||||
| import java.nio.file.attribute.BasicFileAttributes; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import static java.text.MessageFormat.format; | ||||
| 
 | ||||
| /** | ||||
|  * TODO, Move to stitch. | ||||
|  * Created by covers1624 on 18/02/19. | ||||
|  */ | ||||
| public class LineNumberRemapper { | ||||
| 
 | ||||
|     private final Map<String, RClass> lineMap = new HashMap<>(); | ||||
| 
 | ||||
|     public void readMappings(File lineMappings) { | ||||
|         try (BufferedReader reader = new BufferedReader(new FileReader(lineMappings))) { | ||||
|             RClass clazz = null; | ||||
|             String line = null; | ||||
|             int i = 0; | ||||
|             try { | ||||
|                 while ((line = reader.readLine()) != null) { | ||||
|                     if (line.isEmpty()) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     String[] segs = line.trim().split("\t"); | ||||
|                     if (line.charAt(0) != '\t') { | ||||
|                         clazz = lineMap.computeIfAbsent(segs[0], RClass::new); | ||||
|                         clazz.maxLine = Integer.parseInt(segs[1]); | ||||
|                         clazz.maxLineDist = Integer.parseInt(segs[2]); | ||||
|                     } else { | ||||
|                         clazz.lineMap.put(Integer.parseInt(segs[0]), (Integer) Integer.parseInt(segs[1])); | ||||
|                     } | ||||
|                     i++; | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 throw new RuntimeException(format("Exception reading mapping line @{0}: {1}", i, line), e); | ||||
|             } | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("Exception reading LineMappings file.", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void process(Path input, Path output) throws IOException { | ||||
|         Files.walkFileTree(input, new SimpleFileVisitor<Path>() { | ||||
|             @Override | ||||
|             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||||
|                 String rel = input.relativize(file).toString(); | ||||
|                 Path dst = output.resolve(rel); | ||||
|                 Path parent = dst.getParent(); | ||||
|                 if (parent != null) { | ||||
|                     Files.createDirectories(parent); | ||||
|                 } | ||||
|                 String fName = file.getFileName().toString(); | ||||
|                 if (fName.endsWith(".class")) { | ||||
|                     if (Files.exists(dst)) { | ||||
|                         Files.delete(dst); | ||||
|                     } | ||||
|                     String idx = rel.substring(0, rel.length() - 6); | ||||
|                     int dollarPos = idx.indexOf('$');//This makes the assumption that only Java classes are to be remapped. | ||||
|                     if (dollarPos >= 0) { | ||||
|                         idx = idx.substring(0, dollarPos); | ||||
|                     } | ||||
|                     if (lineMap.containsKey(idx)) { | ||||
|                         try (InputStream is = Files.newInputStream(file)) { | ||||
|                             ClassReader reader = new ClassReader(is); | ||||
|                             ClassWriter writer = new ClassWriter(0); | ||||
| 
 | ||||
|                             reader.accept(new LineNumberVisitor(Opcodes.ASM7, writer, lineMap.get(idx)), 0); | ||||
|                             Files.write(dst, writer.toByteArray()); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                 } else { | ||||
|                     Files.copy(file, dst, StandardCopyOption.REPLACE_EXISTING); | ||||
|                 } | ||||
| 
 | ||||
|                 return FileVisitResult.CONTINUE; | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private static class LineNumberVisitor extends ClassVisitor { | ||||
| 
 | ||||
|         private final RClass rClass; | ||||
| 
 | ||||
|         public LineNumberVisitor(int api, ClassVisitor classVisitor, RClass rClass) { | ||||
|             super(api, classVisitor); | ||||
|             this.rClass = rClass; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { | ||||
|             return new MethodVisitor(api, super.visitMethod(access, name, descriptor, signature, exceptions)) { | ||||
|                 @Override | ||||
|                 public void visitLineNumber(int line, Label start) { | ||||
|                     int tLine = line; | ||||
|                     if (tLine <= 0) { | ||||
|                         super.visitLineNumber(line, start); | ||||
|                     } else if (tLine >= rClass.maxLine) { | ||||
|                         super.visitLineNumber(rClass.maxLineDist, start); | ||||
|                     } else { | ||||
|                         Integer matchedLine = null; | ||||
|                         while (tLine <= rClass.maxLine && ((matchedLine = rClass.lineMap.get(tLine)) != null)) { | ||||
|                             tLine++; | ||||
|                         } | ||||
|                         super.visitLineNumber(matchedLine != null ? matchedLine : rClass.maxLineDist, start); | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static class RClass { | ||||
| 
 | ||||
|         private final String name; | ||||
|         private int maxLine; | ||||
|         private int maxLineDist; | ||||
|         private Map<Integer, Integer> lineMap = new HashMap<>(); | ||||
| 
 | ||||
|         private RClass(String name) { | ||||
|             this.name = name; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
		Loading…
	
		Reference in a new issue