raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.23k stars 804 forks source link

Setting value in constructor aliases collections #1642

Closed jimshowalter closed 4 months ago

jimshowalter commented 4 months ago

In https://github.com/raphw/byte-buddy/issues/1638 I thought we had a solution to defining a constructor that initializes a field to a value:

.defineConstructor(Visibility.PUBLIC)
.intercept(
    MethodCall.invoke(getConstructor(entityClass))
        .onSuper()
        .andThen(
            FieldAccessor.ofField(MODIFIED_FIELDS_TRACKER_FIELD_NAME)
                .setsValue(new HashSet<String>())))

But that turns out to put the same collection in all instances of the class, instead of creating a new hash set per instance:

Screenshot 2024-05-13 at 8 07 26 PM

How can I create a new hash set each time?

dogourd commented 4 months ago

try to use

.intercept(MethodCall.invoke(getConstructor(entityClass))
    .onSuper()                   
    .andThen(MethodCall.construct(HashSet.class.getConstructor())
        .setsField(named(MODIFIED_FIELDS_TRACKER_FIELD_NAME))
    )
);
jimshowalter commented 4 months ago

That seems to have worked!

The only thing now is the decompiled constructor isn't calling super. Is that okay?

import lombok.Data;

@Data
public class Foo {
  private String bar;
  private int ignoreMe;
}

public class Foo$Setter$Tracking$ByteBuddy$Subclass extends Foo {
    private final Set<String> _modified$Fields$Tracker = new HashSet();

    public Foo$Setter$Tracking$ByteBuddy$Subclass() {
    }

    static {
        cachedValue$SAlkJfUp$ojd8qn2 = Foo.class.getMethod("setIgnoreMe", Integer.TYPE);
        cachedValue$SAlkJfUp$jj42cs3 = Foo.class.getMethod("setBar", String.class);
    }
}

(Same behavior if lombok is replaced with manually-declared methods.)

raphw commented 4 months ago

You could not load the class if it did not call super, I assume it's simply not printed as this is implicit.

jimshowalter commented 4 months ago

That's my assumption too. It's just a bit disconcerting that the .class bytecode doesn't render showing it. Could also be an IntelliJ limitation. Was going to try some command-line decompiler but jad isn't maintained, fernflower requires cloning their mirror, etc. Javap doesn't appear to be able to reconstruct pretty-printed source.

raphw commented 4 months ago

What does javap show?