[70070 Closed] Problem with reusing generated Java libraries when using VarParameter

I generated a library and tried to use it in Eclipse or view in Decompiler but found errors upon reflecting a class definition. The sample code to reproduce it is:

namespace ClassLibrary1;

interface

uses
  java.util;

type
  Class1 = public class
  private
    method procWithVar( var _basewkt : String ; var _baseno : Integer ; s : String ) ;
  protected
  public
    method DoSomething;
    method DoSomethingWithVar( var _basewkt : String ; var _baseno : Integer; s : String ) ;
  end;
  
implementation

method Class1.DoSomething;
var
  i : Integer ;

  procedure internalWithVar( var _basewkt : String ; var _baseno : Integer; s : String ) ;
  begin
    _baseno := 0 ;
  end ;

begin

end;

method Class1.procWithVar( var _basewkt : String ; var _baseno : Integer; s : String ) ;
begin
  _baseno := 0 ;
end;

method Class1.DoSomethingWithVar( var _basewkt : String ; var _baseno : Integer; s : String ) ;
begin
  _baseno := 0 ;
end;

end.

In Decompiler I get errors like:
private void DoSomething$0(VarParameter _basewkt, @ValueTypeParameter VarParameter _baseno, // INTERNAL ERROR //

The problem seems to be last method parameter s, if I remove it and leave only two var parameters or move as first parameter, a generated jar is used correctly for other tools like Eclipse or NetBeans. Using the library in VS IDE always works fine. Can you look at this issue?

Artur

Thak you for report, logged as 64906: Problem with reusing generated Java libraries when using VarParameter

Looks like the problem is not solved (version 8.x). Here is another rule to make it fail to use with Eclipse reflector.

namespace ClassLibrary1;

interface

uses
  java.util;

type
  Class1 = public class

  protected
  public
    method test ;
    method DoSomethingWithVar(
                               const _visible : Boolean    ;
                                  _proj    : java.awt.Point ;
                               var _sd2 : Integer ;
                               var _sd : Object 
                             )  ;
  end;

implementation


method Class1.DoSomethingWithVar( 
                               const _visible : Boolean    ;
                                  _proj    : java.awt.Point ;
                                var _sd2 : Integer ; 
                                var _sd : Object 
                               ) ;
begin
  _sd := nil;
end;

method Class1.test;
var
  ss2 : Object ;
  ss1 : Integer ;
begin
  DoSomethingWithVar( false, new java.awt.Point(), var ss1, var ss2 );
end;

end.

This time fail is caused by using class type with var as last parameter. If you exchange var parameters so the class type is first, then ordinal type, it will work. Eclipse will recognize and see the class definition in reflector.

Under decompiler it shows

public class Class1
{
  public void DoSomethingWithVar(boolean _visible, Point _proj, @ValueTypeParameter VarParameter<Integer> _sd2, // INTERNAL ERROR //

after params exchange it generates correct code

public class Class1
{
  public void DoSomethingWithVar(boolean _visible, Point _proj, VarParameter<Object> _sd, @ValueTypeParameter VarParameter<Integer> _sd2)
  {
    _sd.Value = null;
  }
  
  public void test()
  {
    Object ss1 = null;
    int ss2 = 0;
    
    VarParameter localVarParameter1 = new VarParameter(ss1);VarParameter localVarParameter2 = new VarParameter(Integer.valueOf(ss2));self.DoSomethingWithVar(false, new Point(), localVarParameter1, localVarParameter2);ss1 = localVarParameter1.Value;ss2 = ((Integer)localVarParameter2.Value).intValue();
  }
}

I really don’t know what difference it makes for Java reflection mechanism. I raise this problem again. var for Java is not good idea but when you need to share code between .net, Delphi and Java, it’s hard to skip it.

Thanks, logged as bugs://70070: Problem with reusing generated Java libraries when using VarParameter

Of course. I’ve logged a new issue.

Thanks. Hope you find a solution :wink:

bugs://70070 got closed as fixed for release Bradbury Class

Great.

The build that’s up now should have a fix for this.

Thanks. Indeed for these simple tests all works fine in other Java IDEs. But I still have some complex classes that are not visible on class tree. I will try to find the reason.

Curious. The issue for this particular one was that the IDE you use was picky about annotations on parameters. IF any parameter has annotations, each parameter needed a list of them, even if they were all empty. the JRE doesn’t mind if the empty ones were missing, but JD (decompiler) and your IDE does.

Do you mean @ValueTypeParameter annotation? So the other IDE doesn’t like var parameters in class API?

Yes. But it wasn’t the annotation itself that made it fail. It was because the annotation was not the parameter. Oxygene didn’t generate an “empty” annotation list for the last parameter.

Do you have other ideas what can cause the class to be invisible in Eclipse? I checked few classes in JD and for some methods I get /* Error */ and java byte pseudocode. Then I discovered that to fix it I need to remove from method code try finally statement.

 procedure TGIS_ViewerWnd.fset_PrintSubtitleFont(
    const _value : Font
  ) ;
  var
    ft : TGIS_Font ;
  begin
    ft := TGIS_Font.Create ;
    //try
      ft.Color := oVwr.PrintSubtitleFont.Color ;
      ft.Name  := _value.Name ;
      ft.Size  := Round( _value.Size ) ;
      oVwr.PrintSubtitleFont.Assign( ft ) ;
   // finally
      FreeObjectNotNilEx( ft ) ;
  //  end ;
  end ;

Then the JD shows correct java code. With try finally get this pseudo (maybe you will understand it

/* Error */
  private void fset_PrintSubtitleFont(Font _value)
  {
    // Byte code:
    //   0: aconst_null
    //   1: astore_2
    //   2: new 836	tatukgis/jdk/TGIS_Font
    //   5: dup
    //   6: invokespecial 838	tatukgis/jdk/TGIS_Font:<init>	()V
    //   9: astore_2
    //   10: aconst_null
    //   11: astore_3
    //   12: aload_2
    //   13: aload_0
    //   14: getfield 222	tatukgis/jdk/TGIS_ViewerWnd:oVwr	Ltatukgis/jdk/TGIS_Viewer;
    //   17: invokevirtual 886	tatukgis/jdk/TGIS_Viewer:getPrintSubtitleFont	()Ltatukgis/jdk/TGIS_Font;
    //   20: getfield 845	tatukgis/jdk/TGIS_Font:Color	I
    //   23: putfield 845	tatukgis/jdk/TGIS_Font:Color	I
    //   26: aload_2
    //   27: aload_1
    //   28: invokevirtual 850	java/awt/Font:getName	()Ljava/lang/String;
    //   31: putfield 853	tatukgis/jdk/TGIS_Font:Name	Ljava/lang/String;
    //   34: aload_2
    //   35: aload_1
    //   36: invokevirtual 856	java/awt/Font:getSize	()I
    //   39: i2d
    //   40: invokestatic 860	tatukgis/rtl/__Global:Round	(D)J
    //   43: l2i
    //   44: putfield 863	tatukgis/jdk/TGIS_Font:Size	I
    //   47: aload_0
    //   48: getfield 222	tatukgis/jdk/TGIS_ViewerWnd:oVwr	Ltatukgis/jdk/TGIS_Viewer;
    //   51: invokevirtual 886	tatukgis/jdk/TGIS_Viewer:getPrintSubtitleFont	()Ltatukgis/jdk/TGIS_Font;
    //   54: aload_2
    //   55: invokevirtual 867	tatukgis/jdk/TGIS_Font:Assign	(Ltatukgis/jdk/TGIS_Font;)V
    //   58: goto +4 -> 62
    //   61: astore_3
    //   62: aload_2
    //   63: invokestatic 311	tatukgis/rtl/__Global:FreeObjectNotNilEx	(Ljava/lang/Object;)V
    //   66: aload_3
    //   67: ifnull +5 -> 72
    //   70: aload_3
    //   71: athrow
    //   72: return
    // Line number table:
    //   Java source line #2039	-> byte code offset #0
    //   Java source line #2041	-> byte code offset #2
    //   Java source line #2043	-> byte code offset #12
    //   Java source line #2044	-> byte code offset #26
    //   Java source line #2045	-> byte code offset #34
    //   Java source line #2054	-> byte code offset #47
    //   Java source line #2056	-> byte code offset #62
    //   Java source line #2058	-> byte code offset #72
    // Local variable table:
    //   start	length	slot	name	signature
    //   0	73	0	self	TGIS_ViewerWnd
    //   0	73	1	_value	Font
    //   0	63	2	ft	TGIS_Font
    //   11	1	3	localObject1	Object
    //   61	10	3	localObject2	Object
    // Exception table:
    //   from	to	target	type
    //   12	58	61	finally
  }

That’s very weird. You said JD fails too? Can you give me this class (or really any other class, as long as it fails). I’m pretty sure plain try/fin doesn’t make it fail.

Sample code in JAVA which cannot be properly disassembled (I included bytecode as well for easy analysis)

Java
public class Class1
{
     public static void Test() {
       int i = 0 ;
       try
       {
           i = 1;
       }
       finally
       {
           i = 2 ;
       }
       i = 3 ;
     }
}

Compiled from “Class1.java”

public class Class1 extends java.lang.Object{
public Class1();
  Code:
   0: aload_0
   1: invokespecial   #1; //Method java/lang/Object."<init>":()V
   4: return

public static void Test();
  Code:
   0: iconst_0
   1: istore_0
   2: iconst_1
   3: istore_0
   4: iconst_2
   5: istore_0
   6: goto 14
   9: astore_1
   10:     iconst_2
   11:     istore_0
   12:     aload_1
   13:     athrow
   14:     iconst_3
   15:     istore_0
   16:     return
  Exception table:
   from   to  target type
     2     4     9   any
     9    10     9   any

}

Analogic code in Oxygene is problematic and significantly more complicated the Java

Oxygene

procedure Class1.Test; 
var
  i : Integer ;
begin
  try
    i := 1;
  finally
    i := 2;
  end;
    i := 3;
end;

Compiled from “c:\tmp\Class1.pas”

public class classlibrary1.Class1 extends java.lang.Object{
public void Test();
  Code:
   0: aconst_null
   1: astore_1
   2: aconst_null
   3: astore_2
   4: new  #2; //class classlibrary1/Class1
   7: dup
   8: invokespecial   #11; //Method "<init>":()V
   11:     astore_1
   12:     goto 16
   15:     astore_2
   16:     aload_2
   17:     ifnull     22
   20:     aload_2
   21:     athrow
   22:     return
  Exception table:
   from   to  target type
     4    12    15   any

public classlibrary1.Class1();
  Code:
  0: aload_0
   1: invokespecial   #18; //Method java/lang/Object."<init>":()V
   4: return

}

However similar try…catch code seems to works fine and Oxygene generated code is just identical

Java

public class Class1
{
     public void Test() {
       int i = 0 ;
       try
       {
           i = 1;
       }
       catch( Exception ex)
       {
           i = 2 ;
       }
       i = 3 ;
     }
}

Compiled from “Class1.java”

public class Class1 extends java.lang.Object{
public Class1();
  Code:
   0: aload_0
   1: invokespecial   #1; //Method java/lang/Object."<init>":()V
   4: return

public void Test();
  Code:
   0: iconst_0
   1: istore_1
   2: iconst_1
   3: istore_1
   4: goto 10
   7: astore_2
   8: iconst_2
   9: istore_1
   10:     iconst_3
   11:     istore_1
   12:     return
  Exception table:
   from   to  target type
     2     4     7   Class java/lang/Exception


}

Oxygene

procedure Class1.Test; 
var
  i : Integer ;
begin
  try
    i := 1;
  except
    i := 2;
  end;
    i := 3;
end;

Compiled from “c:\tmp\Class1.pas”

public class classlibrary1.Class1 extends java.lang.Object{
public void Test();
  Code:
   0: iconst_0
   1: istore_1
   2: aconst_null
   3: astore_2
   4: iconst_1
   5: istore_1
   6: goto 10
   9: astore_2
   10:     iconst_2
   11:     istore_1
   12:     aload_2
   13:     ifnull     18
   16:     aload_2
   17:     athrow
   18:     iconst_3
   19:     istore_1
   20:     return
  Exception table:
   from   to  target type
     4     6     9   any

public classlibrary1.Class1();
  Code:
   0: aload_0
   1: invokespecial   #18; //Method java/lang/Object."<init>":()V
   4: return

}

I tested it using JD.
I will try to send you on email my project and generated jar. Most classes that show disassembly errors are not reflected in Eclipse and errors are reported for methods where try finally was used. Strangely in this simple test the reflection works fine and I can use this class instance.

Oke but the issue wasn’t about decompiling was it, it was using it at all from Eclipse? It’s quite expected not all code constructs can be decompiled by JD/Eclipse.

I sent you email with project.