Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

public class ConstantData {
private static final PropertyContainerTemplate TEMPLATE = PropertyContainerTemplate.builder()
.requireOne(Keys.DOUBLE_VALUE, Keys.CLASS_VALUE)
.requireOne(Keys.INT_VALUE, Keys.DOUBLE_VALUE, Keys.CLASS_VALUE)
.build();

private final PropertyContainer properties;
Expand Down Expand Up @@ -47,6 +47,7 @@ public static Optional<ConstantData> parse(AnnotationHandle handle) {
}

public static class Keys {
public static final PropertyKey<Integer> INT_VALUE = PropertyKey.create("intValue", Integer.class);
public static final PropertyKey<Double> DOUBLE_VALUE = PropertyKey.create("doubleValue", Double.class);
public static final PropertyKey<Type> CLASS_VALUE = PropertyKey.create("classValue", Type.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.sinytra.adapter.env.util.MixinAnnotations;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;

Expand All @@ -14,12 +15,13 @@ public class MixinTypes {
private static final Map<String, MixinType> MIXIN_TYPES = new HashMap<>();

// TODO:
// MixinConstants.MODIFY_ARGS, MixinConstants.MODIFY_CONST, MixinConstants.WRAP_WITH_CONDITION,
// MixinConstants.MODIFY_ARGS, MixinConstants.WRAP_WITH_CONDITION,
// MixinConstants.OVERWRITE (dont forget special modifyTarget handling)
// MixinConstants.ACCESSOR
public static final MixinType INJECT = new InjectMixin();
public static final MixinType MODIFY_VAR = new ModifyVariableMixin();
public static final MixinType MODIFY_ARG = new ModifyArgMixin();
public static final MixinType MODIFY_CONST = new ModifyConstantMixin();
public static final MixinType MODIFY_RET = new ModifyReturnValueMixin();
public static final MixinType REDIRECT = new RedirectMixin();
public static final MixinType WRAP_OP = new WrapOperationMixin();
Expand All @@ -29,6 +31,7 @@ public class MixinTypes {
registerMixinType(Inject.class, INJECT);
registerMixinType(ModifyVariable.class, MODIFY_VAR);
registerMixinType(ModifyArg.class, MODIFY_ARG);
registerMixinType(ModifyConstant.class, MODIFY_CONST);
registerMixinType(MixinAnnotations.MODIFY_RETURN_VAL_INTERNAL_NAME, MODIFY_RET);
registerMixinType(Redirect.class, REDIRECT);
registerMixinType(MixinAnnotations.WRAP_OPERATION_INTERNAL_NAME, WRAP_OP);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.sinytra.adapter.patch.mixin;

import org.sinytra.adapter.env.ctx.MixinContext;
import org.sinytra.adapter.env.param.MethodParameters;
import org.sinytra.adapter.patch.Recipe;
import org.sinytra.adapter.patch.TxResult;
import org.sinytra.adapter.patch.config.Configuration;
import org.sinytra.adapter.patch.config.ConfigurationTemplates;
import org.sinytra.adapter.patch.config.MutableConfiguration;
import org.sinytra.adapter.patch.config.PropertyContainerTemplate;
import org.sinytra.adapter.patch.config.key.MixinKeys;
import org.sinytra.adapter.patch.processor.Processors;
import org.sinytra.adapter.patch.resolver.Resolvers;

import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import static org.sinytra.adapter.env.param.MethodParameters.ParamGroup.CAPTURED_PARAMS;
import static org.sinytra.adapter.env.param.MethodParameters.ParamGroup.SINGLE_ANY;

public class ModifyConstantMixin implements MixinType {
private static final PropertyContainerTemplate TEMPLATE = ConfigurationTemplates.MIXIN_BASE.extend()
.require(MixinKeys.TARGET_CONSTANT)
.pluralKeys(MixinKeys.TARGET_METHOD, MixinKeys.TARGET_CONSTANT)
.build();

@Override
public PropertyContainerTemplate getConfigurationTemplate() {
return TEMPLATE;
}

@Override
public Set<MixinFlag> getFlags() {
return EnumSet.of(MixinFlag.RETURN_TYPE_SENSITIVE);
}

@Override
public TxResult preProcess(MixinContext context, MutableConfiguration clean, Resolvers resolvers, Processors processors) {
clean.setParameters(MethodParameters.create(context.methodNode(), List.of(SINGLE_ANY, CAPTURED_PARAMS)));
return TxResult.SUCCESS;
}

@Override
public TxResult postProcess(MixinContext context, Configuration clean, MutableConfiguration dirty, Recipe recipe) {
if (dirty.getTargetMethod() == null) {
return TxResult.FAIL;
}

dirty.inheritProperyIfAbsent(MixinKeys.TARGET_CONSTANT);
dirty.inheritParameters();
dirty.inheritReturnType();
return TxResult.SUCCESS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.sinytra.adapter.env.ctx.MixinContext;
import org.sinytra.adapter.patch.Recipe;
import org.sinytra.adapter.patch.config.Configuration;
import org.sinytra.adapter.patch.config.key.MixinKeys;
import org.sinytra.adapter.patch.resolver.CompoundResolver;
import org.sinytra.adapter.env.ctx.TargetPair;
import org.sinytra.adapter.util.MethodQualifier;
Expand All @@ -24,7 +25,8 @@ public InjectionPointResolver() {

@Override
protected boolean canApply(Recipe recipe) {
return recipe.dirty().getAtData() == null;
return recipe.dirty().getAtData() == null
&& !recipe.clean().hasProperty(MixinKeys.TARGET_CONSTANT);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ public PipelineMethodTransformer(List<MethodPatch> methodPatches, boolean patche

@Override
public PatchResult apply(MixinContext context, Configuration config) {
boolean hasManualPatch = this.patchResolver.matches(config);
TargetPair cleanTarget = context.methods().findOwnMethodPair(context.cleanLookup(), config.getTargetMethod());
if (cleanTarget == null) return PatchResult.PASS;
if (cleanTarget == null && !hasManualPatch) return PatchResult.PASS;

TargetPair dirtyTarget = context.methods().findOwnMethodPair(context.dirtyLookup(), config.getTargetMethod());
if (!this.patchResolver.matches(config) && !failsDirtyInjectionCheck(context, config, dirtyTarget) && hasValidSlice(context, config, dirtyTarget))
if (!hasManualPatch && !failsDirtyInjectionCheck(context, config, dirtyTarget) && hasValidSlice(context, config, dirtyTarget))
return PatchResult.PASS;

LOGGER.debug(MIXINPATCH, "Considering method {}", context.getMixinId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public interface MethodPatchBuilder {

MethodPatchBuilder modifyTarget(String method);

MethodPatchBuilder modifyOrdinal(int ordinal);

MethodPatchBuilder modifyInjectionPointOrdinal(int ordinal);

MethodPatchBuilder modifyInjectionPoint(String target);

MethodPatchBuilder modifyInjectionPoint(String value, String target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ public MethodPatchBuilder modifyTarget(String method) {
return this;
}

@Override
public MethodPatchBuilder modifyOrdinal(int ordinal) {
this.config.setProperty(MixinKeys.ORDINAL, ordinal);
return this;
}

@Override
public MethodPatchBuilder modifyInjectionPointOrdinal(int ordinal) {
this.configCompleter = this.configCompleter.andThen((clean, dirty) -> {
AtData at = dirty.getAtData() == null ? clean.getAtData() : dirty.getAtData();
dirty.setAtData(at.withOrdinal(ordinal));
});
return this;
}

@Override
public MethodPatchBuilder modifyInjectionPoint(String target) {
// AtData requires value which must be copied at transform time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import org.sinytra.adapter.patch.DynamicPatches;
import org.sinytra.adapter.patch.Patcher;
import org.sinytra.adapter.patch.test_main.mixin.MinecraftMixinPatchTest;
import org.sinytra.adapter.transform.patch.MethodPatch;
import org.sinytra.adapter.types.FieldTypeUsageTransformer;
import org.sinytra.adapter.util.provider.ClassLookup;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.FabricUtil;

import java.util.List;

import static org.sinytra.adapter.env.util.MixinAnnotations.MODIFY_CONST;

public class DynamicMixinPatchTest extends MinecraftMixinPatchTest {
private static final Logger LOGGER = LogUtils.getLogger();

Expand Down Expand Up @@ -45,7 +48,14 @@ public void copyEntries(String from, String to) {
);
patcher = Patcher.builder(patchEnvironment)
.classTransformer(new FieldTypeUsageTransformer())
.methodTransformers(DynamicPatches.methodTransformers(List.of()))
.methodTransformers(DynamicPatches.methodTransformers(List.of(
MethodPatch.builder()
.targetClass("net/minecraft/world/inventory/AnvilMenu")
.targetMethod("createResult")
.targetMixinType(MODIFY_CONST)
.modifyTarget("createResultInternal")
.build()
)))
.build();
}

Expand All @@ -64,6 +74,21 @@ void testExtractUsingCodePath() throws Exception {
);
}

/**
* Reproduces Origins Classes' efficient-repairs mixin, whose integer
* {@code @ModifyConstant} target moves from {@code AnvilMenu#createResult}
* to NeoForge's {@code AnvilMenu#createResultInternal}.
*/
@Test
void testModifyConstantTarget() throws Exception {
assertSameCode(
"org/sinytra/adapter/test/mc_26_1_2/mixin/AnvilMenuMixin",
"halfRepairMaterialCost",
assertTargetMethod(),
assertTargetsConstant()
);
}

@Override
protected LoadResult load(String className, List<String> allowedMethods) throws Exception {
ClassNode patched = loadClass(className);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.sinytra.adapter.test.mc_26_1_2.mixin;

import net.minecraft.world.inventory.AnvilMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

/**
* Equivalent of the Origins Classes efficient-repairs mixin used to verify
* retargeting an integer {@link ModifyConstant} injector for NeoForge.
*/
@Mixin(AnvilMenu.class)
public class AnvilMenuMixin {
@ModifyConstant(
method = "createResult",
constant = @Constant(intValue = 4, ordinal = 0)
)
private int halfRepairMaterialCost(int original) {
return original / 2;
}

@ModifyConstant(
method = "createResultInternal",
constant = @Constant(intValue = 4)
)
private int halfRepairMaterialCostExpected(int original) {
return original / 2;
}
}
Comment thread
dominicbytes marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.sinytra.adapter.patch.test;

import org.junit.jupiter.api.Test;
import org.sinytra.adapter.env.ann.AtData;
import org.sinytra.adapter.patch.config.MutableConfiguration;
import org.sinytra.adapter.patch.config.key.MixinKeys;
import org.sinytra.adapter.patch.mixin.MixinTypes;
import org.sinytra.adapter.transform.patch.MethodPatch;
import org.spongepowered.asm.mixin.injection.ModifyConstant;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;

/**
* Tests extension points used by explicit platform compatibility patches.
*/
class MethodPatchExtensionTest {
/**
* Verifies that standard {@link ModifyConstant} injectors are dispatched to
* the configuration model that resolves their nested {@code @Constant} target.
*/
@Test
void recognizesModifyConstantMixins() {
assertSame(
MixinTypes.MODIFY_CONST,
MixinTypes.getMixinType(ModifyConstant.class.getName().replace('.', '/'))
);
}

/**
* Verifies that an injector ordinal and its nested injection-point ordinal can
* be changed independently without either update discarding the other.
*/
@Test
void updatesMethodAndInjectionPointOrdinalsTogether() {
MethodPatch patch = MethodPatch.builder()
.modifyOrdinal(3)
.modifyInjectionPointOrdinal(1)
.build();
MutableConfiguration clean = MutableConfiguration.create()
.setAtData(AtData.builder("INVOKE").target("Lexample/Target;call()V").build());
MutableConfiguration dirty = MutableConfiguration.create();

patch.configCompleter().accept(clean, dirty);

assertEquals(3, patch.configuration().getProperty(MixinKeys.ORDINAL).orElseThrow());
assertEquals(1, dirty.getAtData().getOrdinal().orElseThrow());
assertEquals("Lexample/Target;call()V", dirty.getAtData().getTargetOrThrow());
}
}