[Jastadd] [bug report] problems in Program.lookupType(String, String)

From: Na, Hyunik <hina_at_kaist.ac.kr>
Date: Thu, 3 May 2012 08:49:59 +0900

Hello JastAddJ developers,

 

I'm happy to report the final entry of this bug report series.

7640 Java files from OpenJDK1.6 are all well compiled except for some
'really bug' cases

(Some java files related to SNMP protocol are not compiled due to the lack
of SnmpOid class definition,

 or for some other reasons, but OpenJDK doesn't seem to compile these files
either.

 And there are some files which have .java extension, but indeed are not
java files

 but templates to produce other java files by text substitution.)

and the single java file with the problem of definite assignment check in
assert statements

which I reported in the previous e-mail, and other 3 java files which load
that single java file.

 

The last bug is as described below:

 

There seems to be problems in the recent update of
Program.lookupType(String, String).

The cause of the problems seems to be:

 

           . Not all the TypeDecls in an already-loaded java file are
examined when a type is looked up.

 

The solution seems to be simply "restoring the old code with some
modification".

 

Let me explain this with an example.

 

First, put the following two files A.java and B.java in a directory.

 

-=-=-= A.java

class A {

  void test() {

    B b = new B(); // **

    C c = new C(); // ***

    b.m(c);

  }

}

-=-=-=

 

-=-=-= B.java

class B {

  void m( C c ) {}

}

 

class C {}

-=-=-=

 

Compiling A.java using JastAddJ rev 9238 produces the following error
messages

 

-=-=-=

Errors:

A.java:4,5:

  Semantic Error: no visible type named C

A.java:4,15:

  Semantic Error: no visible type named C

 

-=-=-=

 

But, compiling using javac or JastAddJ rev 9211 (on which my current work is
based) does not.

When looking up C type, B.java is not examined because it is not C.java and
is not

one in the command line arguments under the recent update in
Program.lookupType() implementation.

(Only the TypeDecl corresponding to B can be queried from B.java,

 So line ** does not cause an error, but *** does. )

 

In the old implementation, when looking up a type, compilation units are
examined in the same way

regardless of whether they are listed in the command line arguments or
loaded during the compilation.

 

Therefore, the above problem does not occur in the old implementation
because C type is queried from B.java.

 

To see another problem, compile B.java to generate B.class and C.class in
the same directory,

and then remove B.class (so that the class B may be still loaded from
B.java)

 

Then, compiling A.java produces the following surprising error message.

 

-=-=-=

Errors:

A.java:5:

  Semantic Error: no method named m(C) in B matches. However, there is a
method m(C)

-=-=-=

 

The cause is that there are two TypeDecl instances for a single type C,

one residing in C.class and the other in B.java.

The first C in the above error message corresponds to the former, and the
second C to the latter.

 

Using -verbose option when compiling A.java shows:

 

-=-=-=

Loading .java file: A.java in 172 ms

...

Loading .java file: B in 1 ms

Loading .class file: C from /home/hina/myWork/ThisJava/test/./C.class in 0
ms

...

-=-=-=

 

C.class is loaded (which I believe is undesirable)

though the TypeDecl for C type could be queried from B.java which is already
loaded.

 

In addition, there is another problem:

 

  . Semantic checks are not applied to library source files.

 

For example, erase the line *** (to avoid the above error messages) and
insert a

semantically wrong code into class B as follows:

 

class B {

  void m( C c ) {}

  void m2( int i ) { short s = i; } // inserted

}

 

Then, compiling A.java produces no error message, but

compiling using javac and JastAddJ 9211 produces:

 

-=-=-= from javac

./B.java:3: possible loss of precision

found : int

required: short

  void m2( int i ) { short s = i; }

                               ^

1 error

-=-=-=

 

-=-=-= from JastAddJ 9211

Errors:

B.java:3:

  Semantic Error: can not assign s of type short a value of type int

-=-=-=

 

I believe all these do not occur in the old implementation of
Program.lookupType().

 

Unfortunately, there is a bug also in the old implementation of
Program.lookupType().

To see that, first switch the lines ** and *** in A.java to get

 

-=-=-= A.java

class A {

  void test() {

    C c = new C(); // ***

    B b = new B(); // **

    b.m(c);

  }

}

-=-=-=

 

and compile the original B.java (without the erroneous m2 method) to get
B.class and C.class

and erase B.class again.

(Now you have A.java, B.java, and C.class in the directory.)

 

Compiling A.java with the JastAddJ rev 9211 (which has the old
implementation of Program.lookupType())

produces the following error message.

 

-=-=-=

Errors:

B.java:5:

  Semantic Error: duplicate member C in compilation unit

-=-=-=

 

This message is wrong: there is no duplicate C's in a compilation unit.

The name check code producing this message:

 

-=-=-=

  public void TypeDecl.nameCheck() {

    if(isTopLevelType() && lookupType(packageName(), name()) != this) {
// ****

      error("duplicate member " + name() + " in compilation unit");

    }

-=-=-=

 

should probably be replaced with something else such as

 

-=-=-=

  public void CompilationUnit.nameCheck() {

    int num = getNumTypeDecl();

    boolean found = false;

    String tyNameI = null;

 

    outer:

    for ( int i = 0 ; i < num-1 ; i++ ) {

      tyNameI = getTypeDecl(i).name();

      for ( int j = i+1 ; j < num ; j++ ) {

        String tyNameJ = getTypeDecl(j).name();

        if ( tyNameI.equals( tyNameJ ) ) {

          found = true;

          break outer;

        }

      }

    }

    if ( found )

     error("duplicate member " + tyNameI + " in compilation unit");

    ...

-=-=-=

 

But, this is not the point.

Even when we forget for a while the if statement at line **** (by commenting
it out),

we have

 

-=-=-=

Errors:

A.java:5:

Semantic Error: no method named m(C) in B matches. However, there is a
method m(C)

-=-=-=

 

which we had above.

 

This is again from duplicate TypeDecl instances for a single type C.

If C is looked up before B, then C is loaded from C.class and the TypeDecl
in the CompilationUnit

C.class becomes the global TypeDecl for C.

(This is why I switched lines ** and *** above.)

But, for the method m's parameter type C, TypeDecl residing in the
CompilationUnit B.java matches

before the global TypeDecl for C.

 

A solution to this problem would be to suppress local type search in

CompilationUnits loaded from library source files.

 

That is,

 

1. Add the following four lines.

 

-=-=-=

  boolean CompilationUnit.fromLib = false;

  void CompilationUnit.setFromLib() { fromLib = true; }

  String CompilationUnit.requestedType = null;

  void CompilationUnit.setRequestedType( String typeName ) { requestedType =
typeName; }

-=-=-=

 

2. Add the following two lines in the (old) Program.lookupType(Strin,
String) in LookupType.jrag

 

-=-=-=

  ...

  CompilationUnit u = getCompilationUnit(fullName);

  if(u != null) {

    u.setFromLib(); // added

    u.setRequestedType( typeName ); // added

    addCompilationUnit(u);

    ...

-=-=-=

 

3. When looking up a type from within a compilation unit, do local search
only when

the compilation unit is not from library or the type is the one requested
when the unit is loaded.

 

-=-=-=

  eq CompilationUnit.getChild().lookupType(String name) {

    SimpleSet set;

    if ( !fromLib || name.equals( requestedType ) ) { // added

      // locally declared types in compilation unit

      set = localLookupType(name);

      if(!set.isEmpty()) return set;

    }

    .

-=-=-=

 

Thank you for reading this long e-mail

and please check this.

 

- Hyunik.
Received on Thu May 03 2012 - 01:50:14 CEST

This archive was generated by hypermail 2.3.0 : Wed Apr 16 2014 - 17:19:06 CEST