Mixin: Illegal ctor redirect raises NPE instead of a useful error
Exception: https://paste.dimdev.org/yilaqohabe.txt Mixin:
@Redirect(method = "<init>(Lnet/minecraft/block/Block;[Lnet/minecraft/block/properties/IProperty;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/state/BlockStateContainer;<init>(Lnet/minecraft/block/Block;[Lnet/minecraft/block/properties/IProperty;Lcom/google/common/collect/ImmutableMap;)V"))
public void init(Block block, IProperty<?>[] properties, ImmutableMap<IUnlistedProperty<?>, Optional<?>> unlistedProperties) {
this.block = block;
ImmutableSortedMap.Builder<String, IProperty<?>> propertiesBuilder = ImmutableSortedMap.naturalOrder();
for (IProperty<?> property : properties) {
validateProperty(block, property);
propertiesBuilder.put(property.getName(), property);
}
this.properties = propertiesBuilder.build();
}
It seems like superCall is null when calling target.indexOf(superCall) in InvokeInjector.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 18 (17 by maintainers)
Commits related to this issue
- 0.7.12 - Better handling of ctor injectors and redirect, fixes #270 — committed to SpongePowered/Mixin by Mumfrey 6 years ago
Wait, what?!?!!?!
The initial example is trying to redirect a SUPER CONSTRUCTOR CALL?
I can’t even.
…
Still doesn’t explain the actual bug but seriously this is some dumb crap.
Can’t see what the point is…
As I stated in my previous comment, Mixin could check that an overwrite/injection into a constructor initializes all final fields once and only once.
As I stated in my previous comment, this is not a problem since the constructor always initializes the fields in the class. But some parts of Mixin such as local variable capture require you to know the bytecode of the target method, so determining an injection point for the constructor is much easier compared to this.
The mixin class should extend the target’s super class. The overwritten constructor will necessarily include a super call to a correct constructor.
That’s my point. You assume that injecting/overwriting constructors would cause problems without actually being aware of any problems.
It doesn’t, simply making a point.
Just making a point, as my second line says.
Field initialization guarantees, field initialization ordering, super and primary constructor calls, etc. There are concrete examples (and cases I’m not aware of) that I’m sure will be provided, but as this isn’t a project I work on, don’t have any at hand.
And here is a link that shows you how to reflectively assign a new value to a
private static finalvariable.It’s almost as if “able to do something” and “should do something” and “will always work” are 3 entirely separate categories of things.
@DemonWav
No, it is just based on the method’s name being
<init>:Removing that check makes it work, I tested it.
This sentence screams such ignorance it’s frightening.
Oh my god. Even just the fact that ctors are called by
invokespecialrather thaninvokevirtual, that alone should tell you that maybe they are at least a little different than a normal public method? ctors have specific semantics and rules that normal methods just don’t have, treating them like any other method will cause problems.Yes, it is unnecessary in this case. I was just redirecting the constructor as a workaround for not being able to overwrite/inject in it.
I am trying to completely remove this from the constructor, and only create instances of StateImplementation as they are needed, to optimize memory usage:
The only reason I can’t do what I need to do is that Mojang decided to put the initialization code for the
BlockStateContainerin the constructor. If they had simply put it in a separate method namedinitwhich was called by the constructor, I would be able to overwrite it, but just because the method I want to inject into is named<init>rather thaninit, Mixin refuses to do so.But they are. Once compiled to bytecode, a constructor is just a method named
<init>that happens to be called after every object creation. The following code:compiles to the following bytecode:
The methods are exactly the same, apart from one of them being named
<init>rather thaninit.Here are the possible “problems” when injecting into/overwriting a constructor:
@Finalfields are assigned to in the overwriting method.Ok so my superctor lookup is failing because this is actually an overloaded ctor. Makes sense, that can be fixed.
You’re entitled to your opinion, but I directly reject your assertion. The inability to modify constructors is a deliberate feature of Mixin.
Consider the things that you can do with Mixin to interact with object construction:
Now consider the things I don’t let you do:
Doing stupid shit with ASM is your prerogative, but I’m not going to endorse or support it. If you can’t do what you need to with the tools in Mixin then you probably shouldn’t be doing it. I don’t even see anything in your example that you couldn’t do without your proposed solution of redirecting the ctor anyway. If you just want to modify the setting of
propertiesthen do so by redirecting the field setter.I accept that the bug here is that I didn’t take into account delegating (overloaded) ctors, and will fix it accordingly. However redirecting constructors is reckless and 100% unnecessary, I wouldn’t recommend doing it in ASM and it’s certainly never going to happen in Mixin.