MixinExtras icon indicating copy to clipboard operation
MixinExtras copied to clipboard

Make @ModifyExpressionValue support FIELD opcode = PUTFIELD, PUTSTATIC

Open Alexdoru opened this issue 8 months ago • 20 comments

I have this code here :

if (bool) {
    this.someField =  someEpression;
} else {
    this.someField = someOtherExpression;
}

I would like to be able to make one handler to changes the code to :

if (bool) {
    this.someField = this.injectHandler(someEpression);
} else {
    this.someField = this.injectHandler(someOtherExpression);
}

Alexdoru avatar Jun 11 '25 16:06 Alexdoru

Would this involve dealing with potential dups, or ignoring them?

cputnam-a11y avatar Jun 11 '25 16:06 cputnam-a11y

You can already do that with either @WrapOperation or @ModifyExpressionValue + @Expression (this.someField = @(?)). Do neither of those fulfil your goal?

LlamaLad7 avatar Jun 11 '25 16:06 LlamaLad7

If I was using ASM I would do the following:

Original bytecode:

// some instructions that leave a variable on the stack of same type as the field
// <--- inject here
// PUTFIELD owner, field name, field type

Edited bytecode:

// some instructions that leave a variable on the stack of same type as the field
// ALOAD 0
// INVOKEVIRTUAL owner, method name, signature = (Lfieldtype;)Lfieldtype;
// PUTFIELD owner, field name, field type

Doing such an operation is non destructive, it can chain and it doesn't allocate any objects. It really is one of the simpliest operations to do with ASM.

I don't know if I am missing something but this doesn't seem to be possible to do with mixins, using @WrapOperation is destructive, it makes a mess in the bytecode and (it allocates objects?).

And I could figure out your suggestion of using @ModifyExpressionValue + @Expression

Alexdoru avatar Jun 11 '25 17:06 Alexdoru

WrapOperation can chain, and the assumption is that the jit will take care of the lambdas

cputnam-a11y avatar Jun 11 '25 17:06 cputnam-a11y

@ModifyExpressionValue + @Expression is effectively

// some instructions that leave a variable on the stack of same type as the field
// <--- inject here
// PUTFIELD owner, field name, field type

the same as that

cputnam-a11y avatar Jun 11 '25 17:06 cputnam-a11y

using @WrapOperation is destructive, it makes a mess in the bytecode and (it allocates objects?).

But @ModifyExpressionValue is exactly what you want, so just use that.

Earthcomputer avatar Jun 11 '25 17:06 Earthcomputer

using @WrapOperation is destructive, it makes a mess in the bytecode and (it allocates objects?).

But @ModifyExpressionValue is exactly what you want, so just use that.

Doesn't work for PUT Image

Alexdoru avatar Jun 11 '25 17:06 Alexdoru

@ModifyExpressionValue + @Expression is effectively

// some instructions that leave a variable on the stack of same type as the field // <--- inject here // PUTFIELD owner, field name, field type the same as that

What is this @Expression thing, I can't find it in my IDE, it's not on the wiki either

Alexdoru avatar Jun 11 '25 17:06 Alexdoru

It is in the wiki. It's in the RCs.

LlamaLad7 avatar Jun 11 '25 17:06 LlamaLad7

Doesn't work for PUT

You are not modifying PUT, you are modifying someEpression or someOtherExpression in your original example.

Earthcomputer avatar Jun 11 '25 18:06 Earthcomputer

@ModifyExpressionValue + @Expression is effectively

// some instructions that leave a variable on the stack of same type as the field // <--- inject here // PUTFIELD owner, field name, field type the same as that

how much stuff does it injects tho

Alexdoru avatar Jun 11 '25 18:06 Alexdoru

it injects a call to your handler method, that takes the value on the stack, and returns a value of the same type.

cputnam-a11y avatar Jun 11 '25 18:06 cputnam-a11y

in fabric mixin, it can even be just

//value on stack
INVOKESTATIC yourMethod
// newValue on stack

cputnam-a11y avatar Jun 11 '25 18:06 cputnam-a11y

but what does the code in the @Mixin class looks like when using @Expression ? The expression stuff look overcomplicated when all I want is to inject 2 bytecodes. Would it make sense to add a new mixin injector that does this @ModifyFieldAllocation or whatever name you prefer.

Alexdoru avatar Jun 11 '25 18:06 Alexdoru

I would be an injector that is non destructive, it can chain, it doesn't allocated any object, pretty straight forward stuff

Alexdoru avatar Jun 11 '25 18:06 Alexdoru

@Definition(id = "someField", field = "LSomeClass;someField:LFieldType;")
@Expression("this.someField = @(?)")
@ModifyExpressionValue(method = "containingMethod", at = @At("MIXINEXTRAS:EXPRESSION"))
private FieldType modifyFieldAssignment(FieldType originalValue) {
   return newValue;
}

Do you have the MinecraftDev plugin for IntelliJ? It can autocomplete all of this stuff.

Earthcomputer avatar Jun 11 '25 18:06 Earthcomputer

Do you have the MinecraftDev plugin for IntelliJ? It can autocomplete all of this stuff.

ok how about this:

@ModifyFieldAllocation(method = "containingMethod", field = "LSomeClass;someField:LFieldType;")
private FieldType modifyFieldAssignment(FieldType originalValue) {
   return newValue;
}

isn't it much more convinient

Alexdoru avatar Jun 11 '25 18:06 Alexdoru

the theoretical @ModifyInput will also be able to do this, but I don't think it's too cumbersome to require targeting via expressions, especially with mcdcv

cputnam-a11y avatar Jun 11 '25 18:06 cputnam-a11y

Anyways I think that @ModifyExpressionValue should just work out of the box with PUTFIELD to modify the value that is allocated to the field, that would be great and much more convinient than using the @Expression stuff. If you do not agree it's whatever.

Alexdoru avatar Jun 11 '25 18:06 Alexdoru

This will be covered by the hypothetical @ModifyInput, I don't want @ModifyExpressionValue to work like this.

LlamaLad7 avatar Jun 12 '25 15:06 LlamaLad7