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' | group = 'net.fabricmc' | ||||||
| archivesBaseName = project.name | archivesBaseName = project.name | ||||||
| version = '0.2.1-SNAPSHOT' | version = '0.2.2-SNAPSHOT' | ||||||
| 
 | 
 | ||||||
| def build = "local" | def build = "local" | ||||||
| def ENV = System.getenv() | def ENV = System.getenv() | ||||||
|  |  | ||||||
|  | @ -136,27 +136,8 @@ public class AbstractPlugin implements Plugin<Project> { | ||||||
| 		configureMaven(); | 		configureMaven(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	public Project getProject() { | ||||||
| 	 * Permit to create a Task instance of the type in the project | 		return 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); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -24,30 +24,110 @@ | ||||||
| 
 | 
 | ||||||
| package net.fabricmc.loom; | 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.*; | ||||||
|  | import net.fabricmc.loom.task.fernflower.FernFlowerTask; | ||||||
|  | import net.fabricmc.loom.util.LineNumberRemapper; | ||||||
| import org.gradle.api.Project; | 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 { | 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 | 	@Override | ||||||
| 	public void apply(Project target) { | 	public void apply(Project target) { | ||||||
| 		super.apply(target); | 		super.apply(target); | ||||||
| 
 | 
 | ||||||
| 		makeTask("cleanLoomBinaries", CleanLoomBinaries.class); | 		TaskContainer tasks = target.getTasks(); | ||||||
| 		makeTask("cleanLoomMappings", CleanLoomMappings.class); | 		 | ||||||
|  | 		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"); | 			task.setInput(mappedJar); | ||||||
| 		makeTask("genEclipseRuns", GenEclipseRunsTask.class).dependsOn("downloadAssets").setGroup("ide"); | 			task.setOutput(sourcesJar); | ||||||
| 		makeTask("vscode", GenVsCodeProjectTask.class).dependsOn("downloadAssets").setGroup("ide"); | 			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"); | 			task.doLast((tt) -> { | ||||||
| 		makeTask("runServer", RunServerTask.class).dependsOn("buildNeeded").setGroup("minecraftMapped"); | 				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. |  * SOFTWARE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package net.fabricmc.loom.task; | package net.fabricmc.loom.task.fernflower; | ||||||
| 
 | 
 | ||||||
| import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; | ||||||
| 
 | 
 | ||||||
| public class LoomFernflowerLogger extends IFernflowerLogger { | /** | ||||||
| 	@Override |  * Literally does nothing. | ||||||
| 	public void writeMessage(String s, Severity severity) { |  * Created by covers1624 on 11/02/19. | ||||||
| 		if (severity == Severity.WARN || severity == Severity.ERROR) { |  */ | ||||||
| 			System.err.println(s); | public class NoopFFLogger extends IFernflowerLogger { | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	@Override |     @Override | ||||||
| 	public void writeMessage(String s, Severity severity, Throwable throwable) { |     public void writeMessage(String message, Severity severity) { | ||||||
| 		if (severity == Severity.WARN || severity == Severity.ERROR) { | 
 | ||||||
| 			System.err.println(s); |     } | ||||||
| 			throwable.printStackTrace(System.err); | 
 | ||||||
| 		} |     @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