Fehlerhafte Typen bei Aufrufen von Interace-Methode mit this-Parameter #307

Closed
opened 2024-03-25 22:09:12 +00:00 by i21023 · 7 comments
Collaborator

Beim Visitor-Pattern vom Compiler ist mir aufgefallen, dass die visit(this){} Methode teilweise mit einem falschen Typen für den Parameter this aufgerufen wird.

Beispiel:

class Main{
    void main(){
        IVisitor v = new Visitor();
        FooBar f = new FooBar();
    
        f.accept(v);
    }
}

interface IVisitor{
    void visit(Foo f);
    void visit(FooBar fb);
}

interface IAcceptor{
    void accept(IVisitor v);
}

class Visitor implements IVisitor{

    @Override
    public void visit(Foo f) {
    }

    @Override
    public void visit(FooBar fb) {
    }
}

class Foo implements IAcceptor{

    @Override
    public void accept(IVisitor v) {
        v.visit(this);
    }
}

class FooBar implements IAcceptor{

    @Override
    public void accept(IVisitor v) {
        v.visit(this);
    }
}

Code

public void accept(IVisitor);
    descriptor: (LIVisitor;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_1
         1: aload_0
         2: invokeinterface #19,  2           // InterfaceMethod IVisitor.visit:(LFooBar;)V
         7: return
         8: athrow
      StackMapTable: number_of_entries = 1
        frame_type = 255 /* full_frame */
          offset_delta = 8
          locals = []
          stack = [ class java/lang/Throwable ]
    Signature: #13                          // (LIVisitor;)V
      JavaTXSignature: length = 0x2 (unknown attribute)
       00 1C

Bytecode der Methode Foo.accept(IVisitor)

Wie man sehen kann wird visit in der Klasse Foo mit dem Typen FooBar statt Foo aufgerufen, was natürlich nicht funktioniert.

Der Bug ist leider auch nicht deterministisch. Bei jedem neuen Kompilieren können sich die Typen des Aufrufs ändern.

Manchmal stimmen der Typ vom visit-Aufruf in Foo nicht, manchmal in FooBar nicht, manchmal sind sogar beide vertauscht, also Foo.visit ruft mit dem Typ FooBar auf und FooBar.visit ruft mit dem Typ Foo auf und manchmal ist auch alles korrekt. Ich hoffe also, dass andere den Bug überhaupt reproduzieren können."

Beim Visitor-Pattern vom Compiler ist mir aufgefallen, dass die `visit(this){} ` Methode teilweise mit einem falschen Typen für den Parameter `this` aufgerufen wird. **Beispiel:** ```java class Main{ void main(){ IVisitor v = new Visitor(); FooBar f = new FooBar(); f.accept(v); } } interface IVisitor{ void visit(Foo f); void visit(FooBar fb); } interface IAcceptor{ void accept(IVisitor v); } class Visitor implements IVisitor{ @Override public void visit(Foo f) { } @Override public void visit(FooBar fb) { } } class Foo implements IAcceptor{ @Override public void accept(IVisitor v) { v.visit(this); } } class FooBar implements IAcceptor{ @Override public void accept(IVisitor v) { v.visit(this); } } ``` *Code* ``` public void accept(IVisitor); descriptor: (LIVisitor;)V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_1 1: aload_0 2: invokeinterface #19, 2 // InterfaceMethod IVisitor.visit:(LFooBar;)V 7: return 8: athrow StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 8 locals = [] stack = [ class java/lang/Throwable ] Signature: #13 // (LIVisitor;)V JavaTXSignature: length = 0x2 (unknown attribute) 00 1C ``` *Bytecode der Methode `Foo.accept(IVisitor)`* Wie man sehen kann wird `visit` in der Klasse `Foo` mit dem Typen `FooBar` statt `Foo` aufgerufen, was natürlich nicht funktioniert. Der Bug ist leider auch **nicht deterministisch**. Bei jedem neuen Kompilieren können sich die Typen des Aufrufs ändern. Manchmal stimmen der Typ vom `visit`-Aufruf in `Foo` nicht, manchmal in `FooBar` nicht, manchmal sind sogar beide vertauscht, also `Foo.visit` ruft mit dem Typ `FooBar` auf und `FooBar.visit` ruft mit dem Typ `Foo` auf und manchmal ist auch alles korrekt. Ich hoffe also, dass andere den Bug überhaupt reproduzieren können."
639 B
dholle referenced this issue from a commit 2024-03-26 10:04:11 +00:00
Owner

Ich kann den Bug leider nicht reproduzieren... Kannst du dir mal den Test anschauen (Bug307.jav)? So funktioniert es bei mir anscheinend jedes mal. Wie genau rufst du das ganze denn auf?

Ich kann den Bug leider nicht reproduzieren... Kannst du dir mal den Test anschauen (Bug307.jav)? So funktioniert es bei mir anscheinend jedes mal. Wie genau rufst du das ganze denn auf?
dholle added the
Codegen
works for me
labels 2024-03-26 10:06:13 +00:00
dholle removed the
works for me
label 2024-03-26 14:48:30 +00:00
Owner

Das Problem tritt anscheinend nur auf wenn der JavaTX compiler mit der Konsole aufgerufen wird. Sehr seltsam.

Das Problem tritt anscheinend nur auf wenn der JavaTX compiler mit der Konsole aufgerufen wird. Sehr seltsam.
Author
Collaborator

Bei mir tritt das Problem immer noch auf. Im Anhang auch mal die resultierenden class files mit den falschen Aufrufen wie oben beschrieben.

Dieses mal habe ich den Compiler auch nicht über das Terminal mit der jar aufgerufen, sondern über die Intellij GUI, falls du das meintest.

grafik


Mit this scheint es aber nichts zu tun zu haben, wenn ich stattdessen ein neu erstelltes Objekt übergebe, tritt das gleiche Problem mit dem falschen Typen im Aufruf auf.


...

class Foo implements IAcceptor{

    @Override
    public void accept(IVisitor v) {
        v.visit(new Foo()); //new Foo() statt this
    }
}

class FooBar implements IAcceptor{

    @Override
    public void accept(IVisitor v) {
        v.visit(new FooBar()); //new FooBar() statt this
    }
}
Bei mir tritt das Problem immer noch auf. Im Anhang auch mal die resultierenden class files mit den falschen Aufrufen wie oben beschrieben. Dieses mal habe ich den Compiler auch nicht über das Terminal mit der jar aufgerufen, sondern über die Intellij GUI, falls du das meintest. ![grafik](/attachments/d198555e-12bd-42e3-b6b6-aa617f070ba1) ___ Mit `this` scheint es aber nichts zu tun zu haben, wenn ich stattdessen ein neu erstelltes Objekt übergebe, tritt das gleiche Problem mit dem falschen Typen im Aufruf auf. ```java ... class Foo implements IAcceptor{ @Override public void accept(IVisitor v) { v.visit(new Foo()); //new Foo() statt this } } class FooBar implements IAcceptor{ @Override public void accept(IVisitor v) { v.visit(new FooBar()); //new FooBar() statt this } } ```
i21023 reopened this issue 2024-03-26 23:13:02 +00:00
dholle reopened this issue 2024-03-27 11:23:18 +00:00
Owner

@pl So wie es aussieht ist hier die Signatur der Methode visit falsch. Es passiert leider nur äußerst selten, also wenn du den Test testBug307 so um die 10x aufrufst sollte ein Fehler kommen.

@pl So wie es aussieht ist hier die Signatur der Methode `visit` falsch. Es passiert leider nur äußerst selten, also wenn du den Test `testBug307` so um die 10x aufrufst sollte ein Fehler kommen.
dholle added
Unify
and removed
Codegen
labels 2024-03-27 11:28:09 +00:00
Author
Collaborator

Ich hab den Testfall mal vereinfacht und die Interfaces entfernt. Vielleicht ist das Problem so besser eingrenzbar.

class Visitor{
    public void visit(Impl1 f) {
    }
    public void visit(Impl2 fb) {
    }
}

class Impl1{
    public void accept(Visitor v) {
        v.visit(this);
    }
}

class Impl2{
    public void accept(Visitor v) {
        v.visit(this);
    }
}
Ich hab den Testfall mal vereinfacht und die Interfaces entfernt. Vielleicht ist das Problem so besser eingrenzbar. ```java class Visitor{ public void visit(Impl1 f) { } public void visit(Impl2 fb) { } } class Impl1{ public void accept(Visitor v) { v.visit(this); } } class Impl2{ public void accept(Visitor v) { v.visit(this); } } ```
Owner

Wenn man sich das Ergebnis anschaut kommt eine unterschiedliche Lösung raus:

Diese hier wäre richtig:

TPH AW = Impl2
...

Impl2()({
  })::TPH BD
  void accept(IVisitor v)({
    ((v)::IVisitor.visit Signature: [TPH AW, TPH AX]((this)::TPH AV))::TPH AY;
    return;
  })::TPH BA

  Impl2()({
    super(());
  })::TPH BC

}

manchmal passiert jedoch

TPH AW = Impl1
Wenn man sich das Ergebnis anschaut kommt eine unterschiedliche Lösung raus: Diese hier wäre richtig: ``` TPH AW = Impl2 ... Impl2()({ })::TPH BD void accept(IVisitor v)({ ((v)::IVisitor.visit Signature: [TPH AW, TPH AX]((this)::TPH AV))::TPH AY; return; })::TPH BA Impl2()({ super(()); })::TPH BC } ``` manchmal passiert jedoch ``` TPH AW = Impl1 ```
Owner

Mit dem commit e37040f367 wurde Bug gefixt.

Bei den parallelen Ausführungen der Oder-Contraints wurden die methodsignatures nicht angepasst.

Mit dem commit e37040f367de8f275245ce85fe5cb942e0786dbe wurde Bug gefixt. Bei den parallelen Ausführungen der Oder-Contraints wurden die methodsignatures nicht angepasst.
pl closed this issue 2024-04-02 22:16:43 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: JavaTX/JavaCompilerCore#307
No description provided.