Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
256a5a68e1 |
No files matched your search
+2
-17
@@ -2,12 +2,11 @@ import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.papermc.paperweight.userdev") version "1.7.2"
|
||||
id("io.papermc.paperweight.userdev")
|
||||
id("xyz.jpenilla.run-paper") version "2.3.0" // Adds runServer and runMojangMappedServer tasks for testing
|
||||
id("xyz.jpenilla.resource-factory-bukkit-convention") version "1.1.1" // Generates plugin.yml based on the Gradle config
|
||||
|
||||
// Shades and relocates dependencies into our plugin jar. See https://imperceptiblethoughts.com/shadow/introduction/
|
||||
id("io.github.goooler.shadow") version "8.1.7"
|
||||
`remap-plugin-src`
|
||||
}
|
||||
|
||||
group = "io.papermc.paperweight"
|
||||
@@ -36,11 +35,6 @@ dependencies {
|
||||
paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT")
|
||||
// paperweight.foliaDevBundle("1.21-R0.1-SNAPSHOT")
|
||||
// paperweight.devBundle("com.example.paperfork", "1.21-R0.1-SNAPSHOT")
|
||||
|
||||
// Shadow will include the runtimeClasspath by default, which implementation adds to.
|
||||
// Dependencies you don't want to include go in the compileOnly configuration.
|
||||
// Make sure to relocate shaded dependencies!
|
||||
implementation("org.incendo", "cloud-paper", "2.0.0-beta.8")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -61,15 +55,6 @@ tasks {
|
||||
outputJar = layout.buildDirectory.file("libs/PaperweightTestPlugin-${project.version}.jar")
|
||||
}
|
||||
*/
|
||||
|
||||
shadowJar {
|
||||
// helper function to relocate a package into our package
|
||||
fun reloc(pkg: String) = relocate(pkg, "io.papermc.paperweight.testplugin.dependency.$pkg")
|
||||
|
||||
// relocate cloud and it's transitive dependencies
|
||||
reloc("org.incendo.cloud")
|
||||
reloc("io.leangen.geantyref")
|
||||
}
|
||||
}
|
||||
|
||||
// Configure plugin.yml generation
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("io.papermc.paperweight:paperweight-userdev:1.7.2")
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Cadix Development (https://www.cadixdev.org)
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which accompanies this distribution,
|
||||
* and is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package modified.mercury;
|
||||
|
||||
import static paper.libs.org.cadixdev.mercury.util.BombeBindings.isPackagePrivate;
|
||||
|
||||
import paper.libs.org.cadixdev.lorenz.MappingSet;
|
||||
import paper.libs.org.cadixdev.lorenz.model.ClassMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.InnerClassMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.Mapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.TopLevelClassMapping;
|
||||
import paper.libs.org.cadixdev.mercury.RewriteContext;
|
||||
import paper.libs.org.cadixdev.mercury.jdt.rewrite.imports.ImportRewrite;
|
||||
import paper.libs.org.cadixdev.mercury.util.GracefulCheck;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AST;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ASTNode;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AnnotatableType;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.EnumDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.IBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.IDocElement;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ImportDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.Javadoc;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.Modifier;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.Name;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.NameQualifiedType;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.PackageDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.QualifiedName;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.QualifiedType;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.SimpleName;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.SimpleType;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.TagElement;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.rewrite.ListRewrite;
|
||||
import paper.libs.org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class RemapperVisitor extends SimpleRemapperVisitor {
|
||||
|
||||
private final ImportRewrite importRewrite;
|
||||
private final Deque<ImportContext> importStack = new ArrayDeque<>();
|
||||
private final String simpleDeobfuscatedName;
|
||||
|
||||
public RemapperVisitor(RewriteContext context, MappingSet mappings, boolean javadoc) {
|
||||
super(context, mappings, javadoc);
|
||||
|
||||
this.importRewrite = context.createImportRewrite();
|
||||
importRewrite.setUseContextToFilterImplicitImports(true);
|
||||
|
||||
TopLevelClassMapping primary = mappings.getTopLevelClassMapping(context.getQualifiedPrimaryType()).orElse(null);
|
||||
if (primary != null) {
|
||||
context.setPackageName(primary.getDeobfuscatedPackage().replace('/', '.'));
|
||||
this.importRewrite.setImplicitPackageName(context.getPackageName());
|
||||
|
||||
this.simpleDeobfuscatedName = primary.getSimpleDeobfuscatedName();
|
||||
context.setPrimaryType(simpleDeobfuscatedName);
|
||||
|
||||
List<String> implicitTypes = new ArrayList<>();
|
||||
String simpleObfuscatedName = primary.getSimpleObfuscatedName();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<AbstractTypeDeclaration> types = context.getCompilationUnit().types();
|
||||
for (AbstractTypeDeclaration type : types) {
|
||||
String name = type.getName().getIdentifier();
|
||||
if (name.equals(simpleObfuscatedName)) {
|
||||
implicitTypes.add(simpleDeobfuscatedName);
|
||||
} else {
|
||||
implicitTypes.add(mappings.getTopLevelClassMapping(context.getPackageName() + '.' + name)
|
||||
.map(Mapping::getSimpleDeobfuscatedName)
|
||||
.orElse(name));
|
||||
}
|
||||
}
|
||||
this.importRewrite.setImplicitTypes(implicitTypes);
|
||||
} else {
|
||||
this.simpleDeobfuscatedName = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void remapType(SimpleName node, ITypeBinding binding) {
|
||||
if (binding.isTypeVariable() || GracefulCheck.checkGracefully(this.context, binding)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (binding.getBinaryName() == null) {
|
||||
throw new IllegalStateException("Binary name for binding " + binding.getQualifiedName() + " is null. Did you forget to add a library to the classpath?");
|
||||
}
|
||||
|
||||
ClassMapping<?, ?> mapping = this.mappings.computeClassMapping(binding.getBinaryName()).orElse(null);
|
||||
|
||||
if (node.getParent() instanceof AbstractTypeDeclaration
|
||||
|| node.getParent() instanceof QualifiedType
|
||||
|| node.getParent() instanceof NameQualifiedType
|
||||
|| binding.isLocal()) {
|
||||
if (mapping != null) {
|
||||
updateIdentifier(node, mapping.getSimpleDeobfuscatedName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String qualifiedName = (mapping != null ? mapping.getFullDeobfuscatedName().replace('/', '.') : binding.getBinaryName()).replace('$', '.');
|
||||
String newName = this.importRewrite.addImport(qualifiedName, this.importStack.peek());
|
||||
|
||||
if (!node.getIdentifier().equals(newName) && !node.isVar()) {
|
||||
if (newName.indexOf('.') == -1) {
|
||||
this.context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, newName, null);
|
||||
} else {
|
||||
// Qualified name
|
||||
this.context.createASTRewrite().replace(node, node.getAST().newName(newName), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remapQualifiedType(QualifiedName node, ITypeBinding binding) {
|
||||
String binaryName = binding.getBinaryName();
|
||||
if (binaryName == null) {
|
||||
if (this.context.getMercury().isGracefulClasspathChecks() || this.context.getMercury().isGracefulJavadocClasspathChecks() && GracefulCheck.isJavadoc(node)) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("No binary name for " + binding.getQualifiedName());
|
||||
}
|
||||
TopLevelClassMapping mapping = this.mappings.getTopLevelClassMapping(binaryName).orElse(null);
|
||||
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String newName = mapping.getDeobfuscatedName().replace('/', '.');
|
||||
if (binaryName.equals(newName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.context.createASTRewrite().replace(node, node.getAST().newName(newName), null);
|
||||
}
|
||||
|
||||
private void remapInnerType(QualifiedName qualifiedName, ITypeBinding outerClass) {
|
||||
final String binaryName = outerClass.getBinaryName();
|
||||
if (binaryName == null) {
|
||||
if (this.context.getMercury().isGracefulClasspathChecks()) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("No binary name for " + outerClass.getQualifiedName());
|
||||
}
|
||||
|
||||
ClassMapping<?, ?> outerClassMapping = this.mappings.computeClassMapping(binaryName).orElse(null);
|
||||
if (outerClassMapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SimpleName node = qualifiedName.getName();
|
||||
InnerClassMapping mapping = outerClassMapping.getInnerClassMapping(node.getIdentifier()).orElse(null);
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateIdentifier(node, mapping.getDeobfuscatedName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visit(SimpleName node, IBinding binding) {
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.TYPE:
|
||||
remapType(node, (ITypeBinding) binding);
|
||||
break;
|
||||
case IBinding.METHOD:
|
||||
case IBinding.VARIABLE:
|
||||
super.visit(node, binding);
|
||||
break;
|
||||
case IBinding.PACKAGE:
|
||||
// This is ignored because it should be covered by separate handling
|
||||
// of QualifiedName (for full-qualified class references),
|
||||
// PackageDeclaration and ImportDeclaration
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled binding: " + binding.getClass().getSimpleName() + " (" + binding.getKind() + ')');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(final TagElement tag) {
|
||||
// We don't want to visit the names of some Javadoc tags, since they can't be remapped.
|
||||
if (TagElement.TAG_LINK.equals(tag.getTagName())) {
|
||||
// With a @link tag, the first fragment will be a name
|
||||
if (tag.fragments().size() >= 1) {
|
||||
final Object fragment = tag.fragments().get(0);
|
||||
|
||||
// A package might be a SimpleName (test), or a QualifiedName (test.test)
|
||||
if (fragment instanceof Name) {
|
||||
final Name name = (Name) fragment;
|
||||
final IBinding binding = name.resolveBinding();
|
||||
|
||||
if (binding != null) {
|
||||
// We can't remap packages, so don't visit package names
|
||||
if (binding.getKind() == IBinding.PACKAGE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.visit(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(QualifiedName node) {
|
||||
IBinding binding = node.resolveBinding();
|
||||
if (binding == null) {
|
||||
if (this.context.getMercury().isGracefulClasspathChecks()) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalStateException("No binding for qualified name node " + node.getFullyQualifiedName());
|
||||
}
|
||||
|
||||
if (binding.getKind() != IBinding.TYPE) {
|
||||
// Unpack the qualified name and remap method/field and type separately
|
||||
return true;
|
||||
}
|
||||
|
||||
Name qualifier = node.getQualifier();
|
||||
IBinding qualifierBinding = qualifier.resolveBinding();
|
||||
switch (qualifierBinding.getKind()) {
|
||||
case IBinding.PACKAGE:
|
||||
// Remap full qualified type
|
||||
remapQualifiedType(node, (ITypeBinding) binding);
|
||||
break;
|
||||
case IBinding.TYPE:
|
||||
// Remap inner type separately
|
||||
remapInnerType(node, (ITypeBinding) qualifierBinding);
|
||||
|
||||
// Remap the qualifier
|
||||
qualifier.accept(this);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected qualifier binding: " + binding.getClass().getSimpleName() + " (" + binding.getKind() + ')');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NameQualifiedType node) {
|
||||
// Annotated inner class -> com.package.Outer.@NonNull Inner
|
||||
// existing mechanisms will handle
|
||||
final IBinding qualBinding = node.getQualifier().resolveBinding();
|
||||
if (qualBinding != null && qualBinding.getKind() == IBinding.TYPE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ITypeBinding binding = node.getName().resolveTypeBinding();
|
||||
if (binding == null) {
|
||||
if (this.context.getMercury().isGracefulClasspathChecks()) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalStateException("No binding for qualified name node " + node.getName());
|
||||
}
|
||||
|
||||
final ClassMapping<?, ?> classMapping = this.mappings.computeClassMapping(binding.getBinaryName()).orElse(null);
|
||||
if (classMapping == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// qualified -> default package (test.@NonNull ObfClass -> @NonNull Core):
|
||||
final String deobfPackage = classMapping.getDeobfuscatedPackage();
|
||||
final ASTRewrite rewrite = this.context.createASTRewrite();
|
||||
if (deobfPackage == null || deobfPackage.isEmpty()) {
|
||||
// if we have annotations, those need to be moved to a new SimpleType node
|
||||
final ASTNode nameNode;
|
||||
if (node.isAnnotatable() && !node.annotations().isEmpty()) {
|
||||
final SimpleType type = node.getName().getAST().newSimpleType((Name) rewrite.createCopyTarget(node.getName()));
|
||||
transferAnnotations(node, type);
|
||||
nameNode = type;
|
||||
} else {
|
||||
nameNode = node.getName();
|
||||
}
|
||||
rewrite.replace(node, nameNode, null);
|
||||
} else {
|
||||
// qualified -> other qualified:
|
||||
rewrite.set(node, NameQualifiedType.QUALIFIER_PROPERTY, node.getAST().newName(deobfPackage.replace('/', '.')), null);
|
||||
}
|
||||
node.getName().accept(this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(PackageDeclaration node) {
|
||||
String currentPackage = node.getName().getFullyQualifiedName();
|
||||
|
||||
if (this.context.getPackageName().isEmpty()) {
|
||||
// remove package declaration if remapped to root package
|
||||
this.context.createASTRewrite().remove(node, null);
|
||||
} else if (!currentPackage.equals(this.context.getPackageName())) {
|
||||
this.context.createASTRewrite().replace(node.getName(), node.getAST().newName(this.context.getPackageName()), null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ImportDeclaration node) {
|
||||
if (node.isStatic()) {
|
||||
// Remap class/member reference separately
|
||||
return true;
|
||||
}
|
||||
|
||||
IBinding binding = node.resolveBinding();
|
||||
if (binding != null) {
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.TYPE:
|
||||
ITypeBinding typeBinding = (ITypeBinding) binding;
|
||||
String name = typeBinding.getBinaryName();
|
||||
if (name == null) {
|
||||
if (this.context.getMercury().isGracefulClasspathChecks()) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalStateException("No binary name for " + typeBinding.getQualifiedName() + ". Did you add the library to the classpath?");
|
||||
}
|
||||
|
||||
ClassMapping<?, ?> mapping = this.mappings.computeClassMapping(name).orElse(null);
|
||||
if (mapping != null && !name.equals(mapping.getFullDeobfuscatedName().replace('/', '.'))) {
|
||||
this.importRewrite.removeImport(typeBinding.getQualifiedName());
|
||||
} else if (this.simpleDeobfuscatedName != null && this.simpleDeobfuscatedName.equals(typeBinding.getName())) {
|
||||
this.importRewrite.removeImport(typeBinding.getQualifiedName());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void pushImportContext(ITypeBinding binding) {
|
||||
ImportContext context = new ImportContext(this.importRewrite.getDefaultImportRewriteContext(), this.importStack.peek());
|
||||
collectImportContext(context, binding);
|
||||
this.importStack.push(context);
|
||||
}
|
||||
|
||||
private void collectImportContext(ImportContext context, ITypeBinding binding) {
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Names from inner classes
|
||||
for (ITypeBinding inner : binding.getDeclaredTypes()) {
|
||||
if (GracefulCheck.checkGracefully(this.context, inner)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int modifiers = inner.getModifiers();
|
||||
if (Modifier.isPrivate(modifiers)) {
|
||||
// Inner type must be declared in this compilation unit
|
||||
if (this.context.getCompilationUnit().findDeclaringNode(inner) == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ClassMapping<?, ?> mapping = this.mappings.computeClassMapping(inner.getBinaryName()).orElse(null);
|
||||
|
||||
if (isPackagePrivate(modifiers)) {
|
||||
// Must come from the same package
|
||||
String packageName = mapping != null ? mapping.getDeobfuscatedPackage() : inner.getPackage().getName();
|
||||
if (!packageName.replace('/', '.').equals(this.context.getPackageName().replace('/', '.'))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String simpleName;
|
||||
String qualifiedName;
|
||||
if (mapping != null) {
|
||||
simpleName = mapping.getSimpleDeobfuscatedName();
|
||||
qualifiedName = mapping.getFullDeobfuscatedName().replace('/', '.').replace('$', '.');
|
||||
} else {
|
||||
simpleName = inner.getName();
|
||||
qualifiedName = inner.getBinaryName().replace('$', '.');
|
||||
}
|
||||
|
||||
if (!context.conflicts.contains(simpleName)) {
|
||||
String current = context.implicit.putIfAbsent(simpleName, qualifiedName);
|
||||
if (current != null && !current.equals(qualifiedName)) {
|
||||
context.implicit.remove(simpleName);
|
||||
context.conflicts.add(simpleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inherited names
|
||||
collectImportContext(context, binding.getSuperclass());
|
||||
for (ITypeBinding parent : binding.getInterfaces()) {
|
||||
collectImportContext(context, parent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AnnotationTypeDeclaration node) {
|
||||
pushImportContext(node.resolveBinding());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AnonymousClassDeclaration node) {
|
||||
pushImportContext(node.resolveBinding());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
pushImportContext(node.resolveBinding());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
pushImportContext(node.resolveBinding());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(AnnotationTypeDeclaration node) {
|
||||
this.importStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(AnonymousClassDeclaration node) {
|
||||
this.importStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(EnumDeclaration node) {
|
||||
this.importStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(TypeDeclaration node) {
|
||||
this.importStack.pop();
|
||||
}
|
||||
|
||||
private void transferAnnotations(final AnnotatableType oldNode, final AnnotatableType newNode) {
|
||||
// we don't support type annotations, ignore
|
||||
if (newNode.getAST().apiLevel() < AST.JLS8) {
|
||||
return;
|
||||
}
|
||||
if (oldNode.annotations().isEmpty()) {
|
||||
// none to transfer
|
||||
return;
|
||||
}
|
||||
|
||||
// transfer and visit
|
||||
final ListRewrite rewrite = this.context.createASTRewrite().getListRewrite(newNode, newNode.getAnnotationsProperty());
|
||||
for (Object annotation : oldNode.annotations()) {
|
||||
final ASTNode annotationNode = (ASTNode) annotation;
|
||||
annotationNode.accept(this);
|
||||
rewrite.insertLast(annotationNode, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImportContext extends ImportRewrite.ImportRewriteContext {
|
||||
private final ImportRewrite.ImportRewriteContext defaultContext;
|
||||
final Map<String, String> implicit;
|
||||
final Set<String> conflicts;
|
||||
|
||||
ImportContext(ImportRewrite.ImportRewriteContext defaultContext, ImportContext parent) {
|
||||
this.defaultContext = defaultContext;
|
||||
if (parent != null) {
|
||||
this.implicit = new HashMap<>(parent.implicit);
|
||||
this.conflicts = new HashSet<>(parent.conflicts);
|
||||
} else {
|
||||
this.implicit = new HashMap<>();
|
||||
this.conflicts = new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findInContext(String qualifier, String name, int kind) {
|
||||
int result = this.defaultContext.findInContext(qualifier, name, kind);
|
||||
if (result != RES_NAME_UNKNOWN) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (kind == KIND_TYPE) {
|
||||
String current = implicit.get(name);
|
||||
if (current != null) {
|
||||
return current.equals(qualifier + '.' + name) ? RES_NAME_FOUND : RES_NAME_CONFLICT;
|
||||
}
|
||||
|
||||
if (conflicts.contains(name)) {
|
||||
return RES_NAME_CONFLICT; // TODO
|
||||
}
|
||||
}
|
||||
|
||||
return RES_NAME_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Cadix Development (https://www.cadixdev.org)
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which accompanies this distribution,
|
||||
* and is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package modified.mercury;
|
||||
|
||||
import static paper.libs.org.cadixdev.mercury.util.BombeBindings.convertSignature;
|
||||
|
||||
import paper.libs.org.cadixdev.bombe.analysis.InheritanceProvider;
|
||||
import paper.libs.org.cadixdev.bombe.type.signature.FieldSignature;
|
||||
import paper.libs.org.cadixdev.bombe.type.signature.MethodSignature;
|
||||
import paper.libs.org.cadixdev.lorenz.MappingSet;
|
||||
import paper.libs.org.cadixdev.lorenz.model.ClassMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.FieldMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.InnerClassMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.MemberMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.MethodMapping;
|
||||
import paper.libs.org.cadixdev.lorenz.model.MethodParameterMapping;
|
||||
import paper.libs.org.cadixdev.mercury.RewriteContext;
|
||||
import paper.libs.org.cadixdev.mercury.analysis.MercuryInheritanceProvider;
|
||||
import paper.libs.org.cadixdev.mercury.util.GracefulCheck;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ASTNode;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.Block;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.IBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.LambdaExpression;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.SimpleName;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.core.dom.VariableDeclaration;
|
||||
import paper.libs.org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Remaps only methods, fields, and parameters.
|
||||
*/
|
||||
public class SimpleRemapperVisitor extends ASTVisitor {
|
||||
|
||||
private static final String LVT_NAMES_PROPERTY = "org.cadixdev.mercury.lvtNames";
|
||||
private static final String LOCAL_VARIABLE_NAME_PROPERTY = "org.cadixdev.mercury.localVariableName";
|
||||
private static final String NEW_PARAM_NAMES_PROPERTY = "org.cadixdev.mercury.newParamNames";
|
||||
|
||||
final RewriteContext context;
|
||||
final MappingSet mappings;
|
||||
private final InheritanceProvider inheritanceProvider;
|
||||
|
||||
public SimpleRemapperVisitor(RewriteContext context, MappingSet mappings, boolean javadoc) {
|
||||
super(javadoc);
|
||||
this.context = context;
|
||||
this.mappings = mappings;
|
||||
this.inheritanceProvider = MercuryInheritanceProvider.get(context.getMercury());
|
||||
}
|
||||
|
||||
final void updateIdentifier(SimpleName node, String newName) {
|
||||
if (!node.getIdentifier().equals(newName) && !node.isVar()) {
|
||||
this.context.createASTRewrite().set(node, SimpleName.IDENTIFIER_PROPERTY, newName, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void remapMethod(SimpleName node, IMethodBinding binding) {
|
||||
ITypeBinding declaringClass = binding.getDeclaringClass();
|
||||
if (GracefulCheck.checkGracefully(this.context, declaringClass)) {
|
||||
return;
|
||||
}
|
||||
final ClassMapping<?, ?> classMapping = this.mappings.getOrCreateClassMapping(declaringClass.getBinaryName());
|
||||
|
||||
if (binding.isConstructor()) {
|
||||
updateIdentifier(node, classMapping.getSimpleDeobfuscatedName());
|
||||
} else {
|
||||
final MethodMapping mapping = findMethodMapping(declaringClass, binding);
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateIdentifier(node, mapping.getDeobfuscatedName());
|
||||
}
|
||||
}
|
||||
|
||||
private void remapField(SimpleName node, IVariableBinding binding) {
|
||||
if (!binding.isField()) {
|
||||
if (binding.isParameter()) {
|
||||
remapParameter(node, binding);
|
||||
} else {
|
||||
checkLocalVariable(node, binding);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ITypeBinding declaringClass = binding.getDeclaringClass();
|
||||
if (declaringClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassMapping<?, ?> classMapping = this.mappings.getClassMapping(declaringClass.getBinaryName()).orElse(null);
|
||||
if (classMapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FieldSignature bindingSignature = convertSignature(binding);
|
||||
FieldMapping mapping = findMemberMapping(bindingSignature, classMapping, ClassMapping::computeFieldMapping);
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateIdentifier(node, mapping.getDeobfuscatedName());
|
||||
}
|
||||
|
||||
private MethodMapping findMethodMapping(ITypeBinding declaringClass, IMethodBinding declaringMethod) {
|
||||
final ClassMapping<?, ?> classMapping = this.mappings.getClassMapping(declaringClass.getBinaryName()).orElse(null);
|
||||
if (classMapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final MethodSignature methodSig = convertSignature(declaringMethod);
|
||||
MethodMapping methodMapping = findMemberMapping(methodSig, classMapping, ClassMapping::getMethodMapping);
|
||||
if (methodMapping == null) {
|
||||
classMapping.complete(this.inheritanceProvider, declaringClass);
|
||||
methodMapping = classMapping.getMethodMapping(methodSig).orElse(null);
|
||||
}
|
||||
|
||||
return methodMapping;
|
||||
}
|
||||
|
||||
private <T extends MemberMapping<?, ?>, M> T findMemberMapping(
|
||||
M matcher,
|
||||
ClassMapping<?, ?> classMapping,
|
||||
BiFunction<ClassMapping<?, ?>, M, Optional<? extends T>> getMapping
|
||||
) {
|
||||
T mapping = getMapping.apply(classMapping, matcher).orElse(null);
|
||||
if (mapping != null) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
if (!this.context.getMercury().isFlexibleAnonymousClassMemberLookups()) {
|
||||
return null;
|
||||
}
|
||||
return findMemberMappingAnonClass(matcher, classMapping, getMapping);
|
||||
}
|
||||
|
||||
private <T extends MemberMapping<?, ?>, M> T findMemberMappingAnonClass(
|
||||
M matcher,
|
||||
ClassMapping<?, ?> classMapping,
|
||||
BiFunction<ClassMapping<?, ?>, M, Optional<? extends T>> getMapping
|
||||
) {
|
||||
// If neither name is different then this method won't do anything
|
||||
if (Objects.equals(classMapping.getObfuscatedName(), classMapping.getDeobfuscatedName())) {
|
||||
return null;
|
||||
}
|
||||
// Anonymous classes must be inner classes
|
||||
if (!(classMapping instanceof InnerClassMapping)) {
|
||||
return null;
|
||||
}
|
||||
// Verify this is inner class is anonymous
|
||||
if (!classMapping.getObfuscatedName().chars().allMatch(Character::isDigit)) {
|
||||
return null;
|
||||
}
|
||||
ClassMapping<?, ?> parentMapping = ((InnerClassMapping) classMapping).getParent();
|
||||
if (parentMapping == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find a sibling anonymous class whose obfuscated name is our deobfuscated name
|
||||
ClassMapping<?, ?> otherClassMapping = parentMapping
|
||||
.getInnerClassMapping(classMapping.getDeobfuscatedName()).orElse(null);
|
||||
if (otherClassMapping != null) {
|
||||
T mapping = getMapping.apply(otherClassMapping, matcher).orElse(null);
|
||||
if (mapping != null) {
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
// Find a sibling anonymous class whose deobfuscated name is our obfuscated name
|
||||
// We have to do something a little less direct for this case
|
||||
for (InnerClassMapping innerClassMapping : parentMapping.getInnerClassMappings()) {
|
||||
if (Objects.equals(classMapping.getObfuscatedName(), innerClassMapping.getDeobfuscatedName())) {
|
||||
otherClassMapping = innerClassMapping;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (otherClassMapping == null) {
|
||||
return null;
|
||||
}
|
||||
return getMapping.apply(otherClassMapping, matcher).orElse(null);
|
||||
}
|
||||
|
||||
private void remapParameter(SimpleName node, IVariableBinding binding) {
|
||||
IMethodBinding declaringMethod = binding.getDeclaringMethod();
|
||||
if (declaringMethod == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
|
||||
ASTNode n = context.getCompilationUnit().findDeclaringNode(declaringMethod);
|
||||
|
||||
if (n instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDeclaration = (MethodDeclaration) n;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SingleVariableDeclaration> parameters = methodDeclaration.parameters();
|
||||
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
if (binding.equals(parameters.get(i).resolveBinding())) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ITypeBinding declaringClass = declaringMethod.getDeclaringClass();
|
||||
if (declaringClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MethodMapping methodMapping = findMethodMapping(declaringClass, declaringMethod);
|
||||
if (methodMapping == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
methodMapping.getParameterMapping(index).ifPresent(paramMapping -> updateIdentifier(node, paramMapping.getDeobfuscatedName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a local variable needs to be renamed because it conflicts with a new parameter name. This will also
|
||||
* attempt to check cases where local variables are defined in lambda expressions.
|
||||
*
|
||||
* @param node The local variable node to check
|
||||
* @param binding The variable binding corresponding to the local variable name
|
||||
*/
|
||||
private void checkLocalVariable(SimpleName node, IVariableBinding binding) {
|
||||
final ASTNode bindingNode = this.context.getCompilationUnit().findDeclaringNode(binding);
|
||||
final String localVariableName = bindingNode != null ? (String) bindingNode.getProperty(LOCAL_VARIABLE_NAME_PROPERTY) : null; // modified
|
||||
if (localVariableName != null) {
|
||||
updateIdentifier(node, localVariableName);
|
||||
return;
|
||||
}
|
||||
|
||||
IMethodBinding declaringMethod = binding.getDeclaringMethod();
|
||||
if (declaringMethod == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (declaringMethod.getDeclaringMember() != null) {
|
||||
// lambda method
|
||||
final LambdaExpression lambdaExpr = getLambdaMethodDeclaration(declaringMethod);
|
||||
if (lambdaExpr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Climb out of declaring stack until we find a method which isn't a lambda
|
||||
IMethodBinding outerMethod = declaringMethod;
|
||||
while (outerMethod.getDeclaringMember() instanceof IMethodBinding) {
|
||||
outerMethod = (IMethodBinding) outerMethod.getDeclaringMember();
|
||||
}
|
||||
if (outerMethod == declaringMethod) {
|
||||
// lookup failed, nothing we can do
|
||||
return;
|
||||
}
|
||||
final ASTNode n = this.context.getCompilationUnit().findDeclaringNode(outerMethod);
|
||||
if (!(n instanceof MethodDeclaration)) {
|
||||
return;
|
||||
}
|
||||
final MethodDeclaration outerDeclaration = (MethodDeclaration) n;
|
||||
|
||||
ASTNode body = lambdaExpr.getBody();
|
||||
// might be an expression
|
||||
if (!(body instanceof Block)) {
|
||||
body = null;
|
||||
}
|
||||
this.checkLocalVariableWithMappings(node, bindingNode, outerMethod, outerDeclaration, declaringMethod, (Block) body);
|
||||
} else {
|
||||
final ASTNode n = context.getCompilationUnit().findDeclaringNode(declaringMethod);
|
||||
if (!(n instanceof MethodDeclaration)) {
|
||||
return;
|
||||
}
|
||||
final MethodDeclaration methodDeclaration = (MethodDeclaration) n;
|
||||
|
||||
this.checkLocalVariableWithMappings(node, bindingNode, declaringMethod, methodDeclaration, declaringMethod, methodDeclaration.getBody());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the given mappings and bindings, check if there are mappings for the method and if any of them conflict
|
||||
* with the given local variable name.
|
||||
*
|
||||
* @param node The local variable name to check
|
||||
* @param bindingNode The binding of the local variable declaration
|
||||
* @param binding The binding of the mapped method to check
|
||||
* @param declaration The declaration node of the mapped method to check
|
||||
* @param blockDeclaringMethod The method binding of the method which defines the {@code block}
|
||||
* @param body The method body to check for local variables
|
||||
*/
|
||||
private void checkLocalVariableWithMappings(
|
||||
SimpleName node,
|
||||
ASTNode bindingNode,
|
||||
IMethodBinding binding,
|
||||
MethodDeclaration declaration,
|
||||
IMethodBinding blockDeclaringMethod,
|
||||
Block body
|
||||
) {
|
||||
final ITypeBinding declaringClass = binding.getDeclaringClass();
|
||||
this.mappings.getClassMapping(declaringClass.getBinaryName())
|
||||
.flatMap(classMapping -> {
|
||||
classMapping.complete(this.inheritanceProvider, declaringClass);
|
||||
return classMapping.getMethodMapping(convertSignature(binding));
|
||||
})
|
||||
.ifPresent(methodMapping -> {
|
||||
if (!methodMapping.getParameterMappings().isEmpty()) {
|
||||
final Set<String> newParamNames = newParamNames(declaration, methodMapping);
|
||||
checkLocalVariableForConflicts(node, bindingNode, blockDeclaringMethod, body, newParamNames);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the method's body defined by {@code methodDeclaration} to collect all local variable names in order to
|
||||
* find a suitable replacement name for {@code node} if it clashes with a name in {@code newParamNames}.
|
||||
*
|
||||
* @param node The local variable node to check
|
||||
* @param bindingNode The binding of the local variable declaration
|
||||
* @param blockDeclaringMethod The method binding of the method which defines the {@code block}
|
||||
* @param block The method body implementation to collect local variable names from
|
||||
* @param newParamNames The set of parameter names after mapping
|
||||
*/
|
||||
private void checkLocalVariableForConflicts(
|
||||
SimpleName node,
|
||||
ASTNode bindingNode,
|
||||
IMethodBinding blockDeclaringMethod,
|
||||
Block block,
|
||||
Set<String> newParamNames
|
||||
) {
|
||||
final String name = node.getIdentifier();
|
||||
if (!newParamNames.contains(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the new param name will screw up this local variable
|
||||
final Set<String> localVariableNames = collectLocalVariableNames(blockDeclaringMethod, block);
|
||||
int counter = 1;
|
||||
String newName = name + counter;
|
||||
while (localVariableNames.contains(newName) || newParamNames.contains(newName)) {
|
||||
counter++;
|
||||
newName = name + counter;
|
||||
}
|
||||
|
||||
localVariableNames.add(newName);
|
||||
bindingNode.setProperty(LOCAL_VARIABLE_NAME_PROPERTY, newName);
|
||||
updateIdentifier(node, newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the declaration of the actual method block for the lambda method
|
||||
*
|
||||
* @param declaringMethod The method binding for the lambda method to check
|
||||
* @return The {@link MethodDeclaration} corresponding to the code block of the lambda implementation
|
||||
*/
|
||||
private LambdaExpression getLambdaMethodDeclaration(IMethodBinding declaringMethod) {
|
||||
final ASTNode node = this.context.getCompilationUnit().findDeclaringNode(declaringMethod.getKey());
|
||||
if (node instanceof LambdaExpression) {
|
||||
return (LambdaExpression) node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the method body of {@code methodDeclaration} and return the set of local variable names defined inside of
|
||||
* it. The set is cached on the {@code methodDeclaration} so it is only computed once.
|
||||
*
|
||||
* @param blockDeclaringMethod The method binding of the method which defines the {@code block}
|
||||
* @param block The method body implementation to check.
|
||||
* @return The set of local variable names defined in the method body.
|
||||
*/
|
||||
private Set<String> collectLocalVariableNames(IMethodBinding blockDeclaringMethod, Block block) {
|
||||
if (block == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<String> result = checkProperty(LVT_NAMES_PROPERTY, block);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
result = new HashSet<>();
|
||||
block.setProperty(LVT_NAMES_PROPERTY, result);
|
||||
|
||||
final IVariableBinding[] synthLocals = blockDeclaringMethod.getSyntheticOuterLocals();
|
||||
for (final IVariableBinding synthLocal : synthLocals) {
|
||||
final String name = synthLocal.getName();
|
||||
if (name.startsWith("val$")) {
|
||||
result.add(name.substring(4));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<ASTNode> statements = (List<ASTNode>) block.statements();
|
||||
for (final ASTNode statement : statements) {
|
||||
if (!(statement instanceof VariableDeclaration)) {
|
||||
continue;
|
||||
}
|
||||
final VariableDeclaration declaration = (VariableDeclaration) statement;
|
||||
result.add(declaration.getName().getIdentifier());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the parameter names defined by the {@code methodDeclaration} and apply any mappings based on the given
|
||||
* {@code mapping}. Return the list of params post-remap.
|
||||
*
|
||||
* @param methodDeclaration The method declaration to check the parameter names on.
|
||||
* @param mapping The mapping to use to determine the new parameter names
|
||||
* @return The set of parameter names after remapping them with {@code mapping}.
|
||||
*/
|
||||
private Set<String> newParamNames(MethodDeclaration methodDeclaration, MethodMapping mapping) {
|
||||
Set<String> result = checkProperty(NEW_PARAM_NAMES_PROPERTY, methodDeclaration);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
result = new HashSet<>();
|
||||
methodDeclaration.setProperty(NEW_PARAM_NAMES_PROPERTY, result);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SingleVariableDeclaration> parameters = methodDeclaration.parameters();
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
final Optional<MethodParameterMapping> paramMapping = mapping.getParameterMapping(i);
|
||||
if (paramMapping.isPresent()) {
|
||||
result.add(paramMapping.get().getDeobfuscatedName());
|
||||
} else {
|
||||
result.add(parameters.get(i).getName().getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given node contains a {@code Set<String>} property named {@code propName} and return it if so.
|
||||
* Returns {@code null} if not.
|
||||
*
|
||||
* @param propName The name of the property
|
||||
* @param node The node to check the property on
|
||||
* @return The set stored on the node or {@code null} if empty
|
||||
*/
|
||||
private static Set<String> checkProperty(String propName, ASTNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
final Object value = node.getProperty(propName);
|
||||
if (value instanceof Set) {
|
||||
@SuppressWarnings("unchecked") final Set<String> result = (Set<String>) value;
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void visit(SimpleName node, IBinding binding) {
|
||||
switch (binding.getKind()) {
|
||||
case IBinding.METHOD:
|
||||
remapMethod(node, ((IMethodBinding) binding).getMethodDeclaration());
|
||||
break;
|
||||
case IBinding.VARIABLE:
|
||||
remapField(node, ((IVariableBinding) binding).getVariableDeclaration());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean visit(SimpleName node) {
|
||||
IBinding binding = node.resolveBinding();
|
||||
if (binding != null) {
|
||||
visit(node, binding);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import modified.mercury.RemapperVisitor
|
||||
import modified.mercury.SimpleRemapperVisitor
|
||||
import paper.libs.org.cadixdev.lorenz.MappingSet
|
||||
import paper.libs.org.cadixdev.mercury.RewriteContext
|
||||
import paper.libs.org.cadixdev.mercury.SourceRewriter
|
||||
import java.util.Objects
|
||||
|
||||
class MercuryRemapper private constructor(mappings: MappingSet, simple: Boolean, javadoc: Boolean) : SourceRewriter {
|
||||
private val mappings: MappingSet
|
||||
private val simple: Boolean
|
||||
private val javadoc: Boolean
|
||||
|
||||
init {
|
||||
this.mappings = Objects.requireNonNull(mappings, "mappings")
|
||||
this.simple = simple
|
||||
this.javadoc = javadoc
|
||||
}
|
||||
|
||||
override fun getFlags(): Int = SourceRewriter.FLAG_RESOLVE_BINDINGS
|
||||
|
||||
override fun rewrite(context: RewriteContext) {
|
||||
context.compilationUnit.accept(if (simple) SimpleRemapperVisitor(context, mappings, this.javadoc) else RemapperVisitor(context, mappings, this.javadoc))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(mappings: MappingSet): SourceRewriter {
|
||||
return MercuryRemapper(mappings, false, true)
|
||||
}
|
||||
|
||||
fun create(mappings: MappingSet, javadoc: Boolean): SourceRewriter {
|
||||
return MercuryRemapper(mappings, false, javadoc)
|
||||
}
|
||||
|
||||
fun createSimple(mappings: MappingSet): SourceRewriter {
|
||||
return MercuryRemapper(mappings, true, true)
|
||||
}
|
||||
|
||||
fun createSimple(mappings: MappingSet, javadoc: Boolean): SourceRewriter {
|
||||
return MercuryRemapper(mappings, true, javadoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import io.papermc.paperweight.tasks.JavaLauncherTask
|
||||
import io.papermc.paperweight.tasks.RemapSources
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.constants.DEOBF_NAMESPACE
|
||||
import io.papermc.paperweight.util.constants.SPIGOT_NAMESPACE
|
||||
import io.papermc.paperweight.util.deleteRecursive
|
||||
import io.papermc.paperweight.util.path
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CompileClasspath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.submit
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import paper.libs.org.cadixdev.mercury.Mercury
|
||||
import paper.libs.org.eclipse.jdt.core.JavaCore
|
||||
import java.nio.file.Files
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class RemapPluginSources : JavaLauncherTask() {
|
||||
@get:CompileClasspath
|
||||
abstract val remapClasspath: ConfigurableFileCollection
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val inputSources: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val mappingsFile: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val addExplicitThis: Property<Boolean>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
addExplicitThis.convention(false)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx2G")
|
||||
forkOptions.executable(launcher.get().executablePath.path.toAbsolutePath().toString())
|
||||
}
|
||||
|
||||
queue.submit(RemapPluginSourcesAction::class) {
|
||||
remapClasspath.setFrom(this@RemapPluginSources.remapClasspath)
|
||||
inputSources.set(this@RemapPluginSources.inputSources)
|
||||
outputDir.set(this@RemapPluginSources.outputDir)
|
||||
mappingsFile.set(this@RemapPluginSources.mappingsFile)
|
||||
addExplicitThis.set(this@RemapPluginSources.addExplicitThis)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class RemapPluginSourcesAction : WorkAction<RemapPluginSourcesAction.Params> {
|
||||
interface Params : WorkParameters {
|
||||
val remapClasspath: ConfigurableFileCollection
|
||||
val inputSources: DirectoryProperty
|
||||
val outputDir: DirectoryProperty
|
||||
val mappingsFile: RegularFileProperty
|
||||
val addExplicitThis: Property<Boolean>
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val mappingSet = MappingFormats.TINY.read(
|
||||
parameters.mappingsFile.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val merc = Mercury()
|
||||
merc.isGracefulClasspathChecks = true
|
||||
merc.sourceCompatibility = JavaCore.VERSION_17
|
||||
merc.classPath.addAll(parameters.remapClasspath.map { it.toPath() })
|
||||
if (parameters.addExplicitThis.get()) {
|
||||
merc.processors.add(RemapSources.ExplicitThisAdder)
|
||||
}
|
||||
merc.processors.add(MercuryRemapper.create(mappingSet, false))
|
||||
|
||||
parameters.outputDir.path.deleteRecursive()
|
||||
Files.createDirectories(parameters.outputDir.path)
|
||||
|
||||
merc.rewrite(parameters.inputSources.path, parameters.outputDir.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.constants.DEOBF_NAMESPACE
|
||||
import io.papermc.paperweight.util.constants.SPIGOT_NAMESPACE
|
||||
import io.papermc.paperweight.util.path
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.nio.file.Files
|
||||
|
||||
abstract class ReverseMappings : io.papermc.paperweight.tasks.BaseTask() {
|
||||
@get:InputFile
|
||||
abstract val inputMappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputMappings: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val fromNs: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val toNs: Property<String>
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val mappingSet = MappingFormats.TINY.read(
|
||||
inputMappings.path,
|
||||
fromNs.get(),
|
||||
toNs.get()
|
||||
).reverse()
|
||||
|
||||
Files.deleteIfExists(outputMappings.path)
|
||||
Files.createDirectories(outputMappings.path.parent)
|
||||
MappingFormats.TINY.write(
|
||||
mappingSet,
|
||||
outputMappings.path,
|
||||
toNs.get(),
|
||||
fromNs.get()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import io.papermc.paperweight.tasks.RemapJar
|
||||
import io.papermc.paperweight.util.constants.DEOBF_NAMESPACE
|
||||
import io.papermc.paperweight.util.constants.SPIGOT_NAMESPACE
|
||||
import io.papermc.paperweight.util.registering
|
||||
|
||||
plugins {
|
||||
java
|
||||
id("io.papermc.paperweight.userdev")
|
||||
}
|
||||
|
||||
tasks {
|
||||
val reverseMappings by registering<ReverseMappings> {
|
||||
inputMappings.set(layout.projectDirectory.file(".gradle/caches/paperweight/setupCache/extractDevBundle.dir/data/mojang+yarn-spigot-reobf.tiny"))
|
||||
outputMappings.set(layout.buildDirectory.file("reversed-reobf-mappings.tiny"))
|
||||
|
||||
fromNs.set(DEOBF_NAMESPACE)
|
||||
toNs.set(SPIGOT_NAMESPACE)
|
||||
}
|
||||
|
||||
val reobfPaperJar by tasks.registering<RemapJar> {
|
||||
// won't be runnable as we don't fixjarforreobf
|
||||
// pretty hacky but should work
|
||||
inputJar.set(layout.file(configurations.mojangMappedServerRuntime.flatMap {
|
||||
it.elements.map { elements ->
|
||||
elements.filter { element ->
|
||||
val p = element.asFile.absolutePath
|
||||
p.contains("ivyRepository") && p.contains("paper-server-userdev")
|
||||
}.single().asFile
|
||||
}
|
||||
}))
|
||||
outputJar.set(layout.buildDirectory.file("reobfPaper.jar"))
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
mappingsFile.set(reverseMappings.flatMap { it.outputMappings })
|
||||
remapper.from(configurations.remapper)
|
||||
}
|
||||
|
||||
@Suppress("unused_variable")
|
||||
val remapPluginSources by registering<RemapPluginSources> {
|
||||
remapClasspath.from(reobfPaperJar.flatMap { it.outputJar })
|
||||
remapClasspath.from(configurations.compileClasspath)
|
||||
inputSources.set(layout.projectDirectory.dir("src/main/java"))
|
||||
outputDir.set(layout.projectDirectory.dir("src/main/mojangMappedJava"))
|
||||
mappingsFile.set(reverseMappings.flatMap { it.outputMappings })
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user