Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {

group 'io.github.notstirred'
archivesBaseName = "dasm"
version '2.5.2'
version '2.5.3'

ext.ossrhUsername = project.findProperty("ossrhUsername") ?: "" // Enter in ~/.gradle/gradle.properties, not here.
ext.ossrhPassword = project.findProperty("ossrhPassword") ?: "" // Enter in ~/.gradle/gradle.properties, not here.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.github.notstirred.dasm.api.annotations.redirect.redirects;

import io.github.notstirred.dasm.api.annotations.Dasm;
import io.github.notstirred.dasm.api.annotations.redirect.sets.InterOwnerContainer;
import io.github.notstirred.dasm.api.annotations.redirect.sets.IntraOwnerContainer;
import io.github.notstirred.dasm.api.annotations.selector.FieldSig;
import io.github.notstirred.dasm.api.annotations.selector.Ref;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.CLASS;

/**
* May be added to a field within a {@link Dasm} class.
* <br/><br/>
* Add a {@link FieldToMethodRedirect} to multiple containers inline at method definition, rather than duplicating the
* method definition inside the containers.
* <br/><br/>
* <h2>Important notes:</h2>
* See {@link FieldToMethodRedirect}'s important notes for more information.
* <h2>Example:</h2>
* This adds a {@link FieldToMethodRedirect} from {@code barX} -> {@code int getBarX()} and {@code void setBarX(int)} to {@code Bar_redirects} which is an {@link IntraOwnerContainer}
* <pre>{@code
* @Dasm
* public class Foo {
* private int foo = 0;
*
* @AddFieldToMethodToSets(
* containers = Bar_redirects.class,
* field = @FieldSig(type = int.class, name = "barX"),
* setter = "setBarX"
* )
* public int getBarX() {
* return this.foo; // This method gets called for every replaced read
* }
* public void setBarX(int barX) {
* this.foo = barX; // This method gets called for every replaced write
* }
* }
* }</pre>
*/
@Target(METHOD)
@Retention(CLASS)
public @interface AddFieldToMethodToSets {
/**
* The containers to add the {@link FieldToMethodRedirect} to.
* <p>Valid container classes are {@link TypeRedirect}, {@link InterOwnerContainer}, {@link IntraOwnerContainer}</p>
*/
Class<?>[] containers();

/**
* The field to replace
*/
FieldSig field();

/**
* The <b>name</b> of the setter method, must have the same owner (be in the same class) as the getter method
* The argument will be inferred from the field and getter.
*/
String setter() default "";

/**
* The source field's mapping owner.
* <br/><br/>
* Only useful if the codebase is remapped and method/field owners are moved<br/>
* Allows specifying the class which owns the field in the mappings
*/
Ref mappingsOwner() default @Ref;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import io.github.notstirred.dasm.annotation.parse.MethodSigImpl;
import io.github.notstirred.dasm.annotation.parse.RedirectSetImpl;
import io.github.notstirred.dasm.annotation.parse.RefImpl;
import io.github.notstirred.dasm.annotation.parse.addtosets.AddFieldToMethodToSetsImpl;
import io.github.notstirred.dasm.annotation.parse.addtosets.AddFieldToSetsImpl;
import io.github.notstirred.dasm.annotation.parse.addtosets.AddMethodToSetsImpl;
import io.github.notstirred.dasm.annotation.parse.redirects.FieldRedirectImpl;
import io.github.notstirred.dasm.annotation.parse.redirects.FieldToMethodRedirectImpl;
import io.github.notstirred.dasm.annotation.parse.redirects.MethodRedirectImpl;
import io.github.notstirred.dasm.api.annotations.Dasm;
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddFieldToSets;
Expand Down Expand Up @@ -34,6 +36,7 @@
import java.util.*;

import static io.github.notstirred.dasm.annotation.AnnotationUtil.*;
import static io.github.notstirred.dasm.util.Format.formatObjectType;

public class AnnotationParser {
private final ClassNodeProvider provider;
Expand Down Expand Up @@ -82,77 +85,100 @@ public NotifyStack findDasmAnnotations(ClassNode targetClass) {
Type targetClassType = Type.getType(TypeUtil.typeNameToDescriptor(targetClass.name));
boolean isTargetInterface = (targetClass.access & Opcodes.ACC_INTERFACE) != 0;

try (NotifyStack classExceptions = NotifyStack.of(targetClass)) {

findRedirectSetsForAnnotation(targetClass.invisibleAnnotations, Dasm.class, "value", classExceptions);
findRedirectSetsForAnnotation(targetClass.invisibleAnnotations, TransformFromClass.class, "sets", classExceptions);

for (FieldNode fieldNode : targetClass.fields) {
try (NotifyStack fieldExceptions = classExceptions.push(fieldNode)) {
findOuterRedirectSetsForAnnotation(fieldNode.invisibleAnnotations, AddFieldToSets.class, "containers", fieldExceptions);

try {
Optional<AddFieldToSetsImpl> optAddToSets = AddFieldToSetsImpl.parse(targetClassType, fieldNode);
if (optAddToSets.isPresent()) {
AddFieldToSetsImpl addToSets = optAddToSets.get();
// All containers for this method must already exist, so we can just use the map
for (Type containerType : addToSets.containers()) {
RedirectSetImpl.Container container = this.containers.get(containerType);
if (container == null) {
fieldExceptions.notifyFromException(new ContainerNotWithinRedirectSet(containerType));
continue;
}
FieldRedirectImpl fieldRedirect = new FieldRedirectImpl(
new ClassField(container.srcType(), addToSets.mappingsOwner().orElse(container.srcType()), addToSets.srcField().type(), addToSets.srcField().name()),
addToSets.dstOwner(),
addToSets.dstMethodName()
);
container.fieldRedirects().add(fieldRedirect);
}
NotifyStack classExceptions = NotifyStack.of(targetClass);

findRedirectSetsForAnnotation(targetClass.invisibleAnnotations, Dasm.class, "value", classExceptions);
findRedirectSetsForAnnotation(targetClass.invisibleAnnotations, TransformFromClass.class, "sets", classExceptions);

for (FieldNode fieldNode : targetClass.fields) {
NotifyStack fieldExceptions = classExceptions.push(fieldNode);
findOuterRedirectSetsForAnnotation(fieldNode.invisibleAnnotations, AddFieldToSets.class, "containers", fieldExceptions);

try {
Optional<AddFieldToSetsImpl> optAddToSets = AddFieldToSetsImpl.parse(targetClassType, fieldNode);
if (optAddToSets.isPresent()) {
AddFieldToSetsImpl addToSets = optAddToSets.get();
// All containers for this method must already exist, so we can just use the map
for (Type containerType : addToSets.containers()) {
RedirectSetImpl.Container container = this.containers.get(containerType);
if (container == null) {
fieldExceptions.notifyFromException(new ContainerNotWithinRedirectSet(containerType));
continue;
}
FieldRedirectImpl fieldRedirect = new FieldRedirectImpl(
new ClassField(container.srcType(), addToSets.mappingsOwner().orElse(container.srcType()), addToSets.srcField().type(), addToSets.srcField().name()),
addToSets.dstOwner(),
addToSets.dstFieldName()
);
container.fieldRedirects().add(fieldRedirect);
}
}
} catch (RefImpl.RefAnnotationGivenNoArguments | MethodSigImpl.InvalidMethodSignature |
MethodSigImpl.EmptySrcName e) {
fieldExceptions.notifyFromException(e);
}
}

for (MethodNode methodNode : targetClass.methods) {
NotifyStack methodExceptions = classExceptions.push(methodNode);
findRedirectSetsForAnnotation(methodNode.invisibleAnnotations, TransformFromMethod.class, "useRedirectSets", methodExceptions);
findOuterRedirectSetsForAnnotation(methodNode.invisibleAnnotations, AddTransformToSets.class, "value", methodExceptions);
findOuterRedirectSetsForAnnotation(methodNode.invisibleAnnotations, AddMethodToSets.class, "containers", methodExceptions);

try {
Optional<AddMethodToSetsImpl> optAddToSets = AddMethodToSetsImpl.parse(targetClassType, isTargetInterface, methodNode);
if (optAddToSets.isPresent()) {
AddMethodToSetsImpl addToSets = optAddToSets.get();
// All containers for this method must already exist, so we can just use the map
for (Type containerType : addToSets.containers()) {
RedirectSetImpl.Container container = this.containers.get(containerType);
if (container == null) {
methodExceptions.notifyFromException(new ContainerNotWithinRedirectSet(containerType));
continue;
}
} catch (RefImpl.RefAnnotationGivenNoArguments | MethodSigImpl.InvalidMethodSignature |
MethodSigImpl.EmptySrcName e) {
fieldExceptions.notifyFromException(e);

MethodRedirectImpl methodRedirect = new MethodRedirectImpl(
new ClassMethod(container.srcType(), addToSets.mappingsOwner().orElse(container.srcType()), addToSets.srcMethod()),
addToSets.dstOwner(),
addToSets.dstMethodName(),
addToSets.isStatic(),
addToSets.isDstInterface()
);
container.methodRedirects().add(methodRedirect);
}
}
} catch (RefImpl.RefAnnotationGivenNoArguments | MethodSigImpl.InvalidMethodSignature |
MethodSigImpl.EmptySrcName e) {
methodExceptions.notifyFromException(e);
}

for (MethodNode methodNode : targetClass.methods) {
try (NotifyStack methodExceptions = classExceptions.push(methodNode)) {
findRedirectSetsForAnnotation(methodNode.invisibleAnnotations, TransformFromMethod.class, "useRedirectSets", methodExceptions);
findOuterRedirectSetsForAnnotation(methodNode.invisibleAnnotations, AddTransformToSets.class, "value", methodExceptions);
findOuterRedirectSetsForAnnotation(methodNode.invisibleAnnotations, AddMethodToSets.class, "containers", methodExceptions);

try {
Optional<AddMethodToSetsImpl> optAddToSets = AddMethodToSetsImpl.parse(targetClassType, isTargetInterface, methodNode);
if (optAddToSets.isPresent()) {
AddMethodToSetsImpl addToSets = optAddToSets.get();
// All containers for this method must already exist, so we can just use the map
for (Type containerType : addToSets.containers()) {
RedirectSetImpl.Container container = this.containers.get(containerType);
if (container == null) {
methodExceptions.notifyFromException(new ContainerNotWithinRedirectSet(containerType));
continue;
}

MethodRedirectImpl methodRedirect = new MethodRedirectImpl(
new ClassMethod(container.srcType(), addToSets.mappingsOwner().orElse(container.srcType()), addToSets.srcMethod()),
addToSets.dstOwner(),
addToSets.dstMethodName(),
addToSets.isStatic(),
addToSets.isDstInterface()
);
container.methodRedirects().add(methodRedirect);
}
try {
Optional<AddFieldToMethodToSetsImpl> optAddToSets = AddFieldToMethodToSetsImpl.parse(targetClassType, methodNode);
if (optAddToSets.isPresent()) {
AddFieldToMethodToSetsImpl addToSets = optAddToSets.get();
// All containers for this method must already exist, so we can just use the map
for (Type containerType : addToSets.containers()) {
RedirectSetImpl.Container container = this.containers.get(containerType);
if (container == null) {
methodExceptions.notifyFromException(new ContainerNotWithinRedirectSet(containerType));
continue;
}
} catch (RefImpl.RefAnnotationGivenNoArguments | MethodSigImpl.InvalidMethodSignature |
MethodSigImpl.EmptySrcName e) {
methodExceptions.notifyFromException(e);
FieldToMethodRedirectImpl fieldToMethodRedirect = new FieldToMethodRedirectImpl(
new ClassField(container.srcType(), addToSets.mappingsOwner().orElse(container.srcType()), addToSets.srcField().type(), addToSets.srcField().name()),
addToSets.dstMethod(),
addToSets.dstSetterMethod(),
addToSets.isStatic(),
(targetClass.access & Opcodes.ACC_INTERFACE) != 0
);
container.fieldToMethodRedirects().add(fieldToMethodRedirect);
}
}
} catch (RefImpl.RefAnnotationGivenNoArguments | MethodSigImpl.InvalidMethodSignature |
MethodSigImpl.EmptySrcName e) {
methodExceptions.notifyFromException(e);
}
return classExceptions;
}
return classExceptions;
}

private void findRedirectSetsForAnnotation(List<AnnotationNode> annotations, Class<?> annotationClass, String setsAnnotationField,
Expand Down Expand Up @@ -262,19 +288,19 @@ private ClassNode getContainingRedirectSetClassNode(Type containerType) throws N

public static class ContainerNotWithinRedirectSet extends DasmException {
public ContainerNotWithinRedirectSet(Type containerType) {
super(String.format("Container `" + containerType.getClassName() + "` must be within a @RedirectSet interface"));
super(String.format("Container `" + formatObjectType(containerType) + "` must be within a @RedirectSet interface"));
}
}

public static class TypeIsNotAContainer extends DasmException {
public TypeIsNotAContainer(Type type) {
super(String.format("Type `" + type.getClassName() + "` is not a container but is used as one"));
super(String.format("Type `" + formatObjectType(type) + "` is not a container but is used as one"));
}
}

public static class NoValidRedirectSetExists extends Notification {
public NoValidRedirectSetExists(Type redirectSetType) {
super(String.format("No valid redirect set exists matching `" + redirectSetType.getClassName() + "`"));
super(String.format("No valid redirect set exists matching `" + formatObjectType(redirectSetType) + "`"));
}
}
}
Loading