Re: Another reflection bug; compatibility results

2000-06-21 Thread Stuart Ballard


Stuart Ballard wrote:
 
 "Edouard G. Parmelan" wrote:
 
  Could you add value of serialVersionUID in Japize ?
 
 Hey, great idea :) I'll try to get this done by tomorrow (my hacking
 gets done on the train to and from work).

Well, it took a little longer than I thought due to Real World work
going crazy (gr) but the versions of Japize and japicompat up at
http://stuart.wuffies.net/japi/ now handle this case correctly. Checking
svuids needs to be turned on explicitly by passing -v to japicompat
(otherwise you'd get a lot of spam if an implementation hadn't concerned
itself with this issue). The results for jdk11-vs-kaffe are at
http://stuart.wuffies.net/japi/v-results-jdk11-kaffe.txt . 108 classes
fail this check, bringing Kaffe up to 248 total errors.

Enjoy :)

Stuart.

PS At some point soon I'll install JDK1.2 and produce the results for
kaffe-vs-jdk12. When I do I'll post the URL of the results to the list.
If anyone beats me to it, feel free to send me jdk12.japi or jdk13.japi
:)



Re: Another reflection bug; compatibility results

2000-06-16 Thread Edouard G. Parmelan


Stuart Ballard wrote:

  To my knowledge, even though static final constants are part of the
  public API, their values are not given by Sun and so they have to be
  determined experimentally.
 
 Exactly why I want to report them as part of Japize... to make it
 possible to automate testing that Kaffe (and GNU Classpath) has gotten
 all the values right.

Could you add value of serialVersionUID in Japize ?

The following code will retreive/compute this value:

ObjectStreamClass stream = ObjectStreamClass.lookup(clz);
if (stream != null) {
// this class is Serializable, register serialVersionUID
emitSerialVersionUID(stream.getSerialVersionUID());
}

Thanks.
-- 
Edouard G. Parmelan
http://egp.free.fr



Re: Another reflection bug; compatibility results

2000-06-16 Thread Stuart Ballard


"Edouard G. Parmelan" wrote:
 
 Could you add value of serialVersionUID in Japize ?
 
 The following code will retreive/compute this value:
 
 ObjectStreamClass stream = ObjectStreamClass.lookup(clz);
 if (stream != null) {
 // this class is Serializable, register serialVersionUID
 emitSerialVersionUID(stream.getSerialVersionUID());
 }

Hey, great idea :) I'll try to get this done by tomorrow (my hacking
gets done on the train to and from work).

Stuart.



Re: Another reflection bug; compatibility results

2000-06-15 Thread Godmar Back


 
 
 Godmar Back wrote:
  
   Have you tried calling 1.2's Class.forName(,false,) and then
  using getField().getValue() on a final static field?
  Does doing this invoke clinit?
 
 I need to be able to run on 1.1...

In 1.1 w/ reflection, you're obviously out of luck since 
Class.forName will call clinit

 
 This is a fairly specific situation that is probably extremely rare. My
 program attempts to print out a description of the public API of a class
 in a machine-readable format that can then be compared to other such
 descriptions. Compile-time constants are part of the public API, other
 fields' values (even public static final ones) are not. Reflection
 doesn't let me distinguish between the two cases.

Whether or not something is a compile-time constant or not is of no
relevance IMO.

For instance, a class may define a public static final int K = 5;
A compiler is free to inline the 5 where it sees K, but not required to do so.
The compiler may as well produce a getfield instruction.

To my knowledge, even though static final constants are part of the
public API, their values are not given by Sun and so they have to be
determined experimentally.

- Godmar




Re: Another reflection bug; compatibility results

2000-06-15 Thread Godmar Back



 Okay, try it again now.

We now also search superinterfaces if you invoke Class.getMethod() on
an interface class.

However, since there's no notion of superinterfaces at the bytecode level,
there's also no ordering - this means that I believe that the result of 
such a getMethod call is not and in fact cannot be well-defined.  We may 
or may not match what the JDK returns.  I hadn't realized that before.

Also, since there no ordering of superinterfaces, there's also no notion 
of "overriding" - hence both Kaffe and JDK report methods in all 
superinterfaces.  However, getMethod will stop looking at the first
match.  This leads (in both JDK and Kaffe) to what you call "Non-equality"
in your test.

However, I believe that none of this should have an adverse impact 
on the interface compatibility checking you're doing.  For instance,
you should be able to easily figure out what's what by checking whether
a class inherits a method or not - one way to find that out is to 
check whether getClass().getMethod().getClass() != getClass().

- Godmar





Re: Another reflection bug; compatibility results

2000-06-15 Thread Stuart Ballard


Godmar Back wrote:
 
  Okay, try it again now.
 
 We now also search superinterfaces if you invoke Class.getMethod() on
 an interface class.

Excellent. Kaffe now gives exactly 0 "M" (the NoSuchMethodException) and
only about 7 or 8 "!" (the non-equality) on the whole of its class
libraries. As you point out, it's probably impossible to avoid all "!"s.
Thanks for explaining why I was getting these in the JDK :)

 Also, since there no ordering of superinterfaces, there's also no notion
 of "overriding" - hence both Kaffe and JDK report methods in all
 superinterfaces.  However, getMethod will stop looking at the first
 match.  This leads (in both JDK and Kaffe) to what you call "Non-equality"
 in your test.

Hmm, okay. That doesn't seem to be a problem for what I'm doing. Since I
only want each distinct method reported once, and I don't care which
class it's defined on (I don't even report that information), I'll
continue to use the inequality test as a way to ensure I don't see any
methods twice. It is safe to assume that two calls to getMethod() with
the same arguments *in the same run of Kaffe* (and without the
possibility of any classes being gc'd) will always return the same
method, right?

 However, I believe that none of this should have an adverse impact
 on the interface compatibility checking you're doing.  For instance,
 you should be able to easily figure out what's what by checking whether
 a class inherits a method or not - one way to find that out is to
 check whether getClass().getMethod().getClass() != getClass().

Actually, I don't even care whether it's inherited or not - I just
didn't want the same method showing up twice. I only bothered to put the
check in at all as a workaround for the fact that Kaffe was reporting
overridden methods all the way up, and I wanted to make sure I only got
the "definitive" one. I'm glad I did or I'd never have realized that the
same method could *legitimately* show up twice if it's inherited from
two superinterfaces.

Thanks for your time :)

Stuart.



Re: Another reflection bug; compatibility results

2000-06-15 Thread Stuart Ballard


Godmar Back wrote:
 
 In 1.1 w/ reflection, you're obviously out of luck since
 Class.forName will call clinit

Blah. I guess I should explore options that look at the bytecode, rather
than reflection, then... (the most important reason I need to run on 1.1
is to get reliable docs *for* 1.1)

 Whether or not something is a compile-time constant or not is of no
 relevance IMO.
 
 For instance, a class may define a public static final int K = 5;
 A compiler is free to inline the 5 where it sees K, but not required to do so.
 The compiler may as well produce a getfield instruction.

Unfortunately it does make a difference. The only reason primitive
constants are part of the public API is because they *can* be inlined
(code compiled against the JDK could break on Kaffe if the compiler had
inlined the value of something that kaffe defined differently; it
doesn't matter that the compiler is free to *not* do this - just the
possibility that it can is enough). If the value is not a primitive
constant, it couldn't be inlined, so a getfield call would *have* to be
issued... and the right value would be "got" on both VMs even if they
were different.

 To my knowledge, even though static final constants are part of the
 public API, their values are not given by Sun and so they have to be
 determined experimentally.

Exactly why I want to report them as part of Japize... to make it
possible to automate testing that Kaffe (and GNU Classpath) has gotten
all the values right.

Stuart.



Re: Another reflection bug; compatibility results

2000-06-15 Thread Godmar Back


 methods twice. It is safe to assume that two calls to getMethod() with
 the same arguments *in the same run of Kaffe* (and without the
 possibility of any classes being gc'd) will always return the same
 method, right?

Right.

- Godmar




Re: Another reflection bug; compatibility results

2000-06-14 Thread Edouard G. Parmelan


Stuart Ballard wrote:

 Well, I've made some changes to the class since I "announced" it here a
 while back and put up a homepage at http://stuart.wuffies.net/japi/ .
 The reason I mention this is (1) I'd like feedback, and (2) I have the
 results of running japicompat between jdk11 and kaffe, and there are 425
 reported errors, although most of them are duplicated several times
 because they are errors in java.awt.Component or java.awt.event.AWTEvent
 and they get reported for every subclass (I'd like to eliminate these
 duplicates but I can't think of an easy way to do it).

I think you can use c.getDeclaredXXX() in place of c.getXXX() to get
ride of inherited fields/methods.


Raw idea:  Did you think it's possible to use a ClassLoader to load
jdk1.1 classes and an other to load Kaffe (or GNU Classpath) classes ?

If so, you could have a one pass comparison framework.

An other solution could be to use a classes manipulations packages as
at.dms.classfile or gnu.bytecode.  Both use GPL Licences.
-- 
Edouard G. Parmelan
http://egp.free.fr



Re: Another reflection bug; compatibility results

2000-06-14 Thread Stuart Ballard


"Edouard G. Parmelan" wrote:
 
 Stuart Ballard wrote:
 
  Well, I've made some changes to the class since I "announced" it here a
  while back and put up a homepage at http://stuart.wuffies.net/japi/ .
  The reason I mention this is (1) I'd like feedback, and (2) I have the
  results of running japicompat between jdk11 and kaffe, and there are 425
  reported errors, although most of them are duplicated several times
  because they are errors in java.awt.Component or java.awt.event.AWTEvent
  and they get reported for every subclass (I'd like to eliminate these
  duplicates but I can't think of an easy way to do it).
 
 I think you can use c.getDeclaredXXX() in place of c.getXXX() to get
 ride of inherited fields/methods.

Right, but it's legal for an implementation to move a method up to a
superclass, so I need to include superclasses' methods in the output
listing or you could get bogus errors. The removal of duplicates would
have to happen in japicompat.pl. I know how to do this but it's not
trivial; basically I need to sort the input so that every class is
processed before its subclasses, and then test each error to see whether
the same error occurred on the superclass. It's on my todo list (and
even on the webpage as of a few minutes ago...)

I do want to get what I have commented better before I start adding
features like that though; it would take a fairly large rewrite.

 Raw idea:  Did you think it's possible to use a ClassLoader to load
 jdk1.1 classes and an other to load Kaffe (or GNU Classpath) classes ?

Yes, but URLClassLoader is 1.2 only and I'm using 1.1 primarily. I could
write a ClassLoader from scratch, but that sounds hard! Also, I'm not
sure that I want to know what would happen if I tried to load
java.lang.Object from a classloader, and there are issues with native
code; I can only imagine what would happen if I tried to load Kaffe's
java.lang.Object onto the JDK with its native code intact...

 If so, you could have a one pass comparison framework.

Originally it bothered me that I couldn't provide that, but now I'm
beginning to think that this way is better. After all, I can distribute
a ~1Mb jdk11.japi file and save all my users downloading the full ~8Mb
of JDK1.1 (the gains for 1.2 and 1.3 are presumably bigger still); it
also makes it possible to run testing on a machine containing only free
software, so long as you have a copy of the japi file generated by
JDK1.1.

 An other solution could be to use a classes manipulations packages as
 at.dms.classfile or gnu.bytecode.  Both use GPL Licences.

That's another approach, but Japize works pretty well at this point -
most of the work I want to do is on japicompat. The last 3 or 4 major
changes I've added to Japize have been Zip support, command line
changes, filesystem support, and so on... the actual API outputting has
been pretty stable for a whole 3 days of heavy hacking on the rest of
it. I'd need a pretty convincing reason to drop Japize and use something
else. If you're interested in pursuing one of the other approaches, by
all means go for it; if it works within the same framework as Japize and
japicompat then I'd certainly be happy to integrate the code. I do have
a slight twinge of regret that as things stand now I can't ever produce
a jdk10.japi...

Thanks for the input :)
Stuart.



Re: Another reflection bug; compatibility results

2000-06-14 Thread Edouard G. Parmelan


Stuart Ballard wrote:

  Raw idea:  Did you think it's possible to use a ClassLoader to load
  jdk1.1 classes and an other to load Kaffe (or GNU Classpath) classes ?
 
 Yes, but URLClassLoader is 1.2 only and I'm using 1.1 primarily. I could
 write a ClassLoader from scratch, but that sounds hard! Also, I'm not
 sure that I want to know what would happen if I tried to load
 java.lang.Object from a classloader, and there are issues with native
 code; I can only imagine what would happen if I tried to load Kaffe's
 java.lang.Object onto the JDK with its native code intact...

For now, Japize does not extrate final values (or I missing something)
so with method Class.forName(String name, boolean initialize, ClassLoader
loader) [yes it's 1.2] clinit is never called, so no native code :-)

  If so, you could have a one pass comparison framework.
 
 Originally it bothered me that I couldn't provide that, but now I'm
 beginning to think that this way is better. After all, I can distribute
 a ~1Mb jdk11.japi file and save all my users downloading the full ~8Mb
 of JDK1.1 (the gains for 1.2 and 1.3 are presumably bigger still); it
 also makes it possible to run testing on a machine containing only free
 software, so long as you have a copy of the japi file generated by
 JDK1.1.

You'r right, ~1Mb is better.

 I do have a slight twinge of regret that as things stand now I can't
 ever produce a jdk10.japi...

I will try to write a small ClassLoader to use with Kaffe to generate
jdk10.japi.
-- 
Edouard G. Parmelan
http://egp.free.fr



Re: Another reflection bug; compatibility results

2000-06-14 Thread Stuart Ballard


"Edouard G. Parmelan" wrote:
 
 For now, Japize does not extrate final values (or I missing something)
 so with method Class.forName(String name, boolean initialize, ClassLoader
 loader) [yes it's 1.2] clinit is never called, so no native code :-)

Actually, Japize does extract all field values that are public, static,
final, and of a primitive type or String. The intent is to catch
primitive constants, since the values have to be the same for
compatibility. However, there are two possible errors here; one is that
being public, static and final is not actually sufficient to be a
primitive constant; the field also has to be initialized to a
compile-time constant value. I'm hoping that it's rare for classes to
have public static final primitive-typed variables that are not compile
time constants. The other possible error is that null is a compile-time
constant, even for non-String objects - Japize would not catch this.
This also seems unlikely - why would you declare a public variable if
it's going to be a compile-time constant null? java.lang.reflect.Field
needs an isPrimitiveConstant() method...

However, I think that asking for the value of a field, even a public
static final one, is enough to cause clinit to get called... right? :(

  I do have a slight twinge of regret that as things stand now I can't
  ever produce a jdk10.japi...
 
 I will try to write a small ClassLoader to use with Kaffe to generate
 jdk10.japi.

That would be awesome :) I was thinking about perl hacks on the output
of javap... ;)

Stuart.



Re: Another reflection bug; compatibility results

2000-06-14 Thread Artur Biesiadowski


Stuart Ballard wrote:

 However, I think that asking for the value of a field, even a public
 static final one, is enough to cause clinit to get called... right? :(


It depends if it is compile time constant. If yes, then clinit will not
be called. From JLS 12.4.1

[when initialization occurs]
[...]
A non-constant field declared in T (rather than inherited from a
superclass or superinterface) is used or assigned. A constant field is
 one that is (explicitly or implicitly) both final and static, and
that is initialized with the value of a compile-time constant expression
 (§15.27). Java specifies that a reference to a constant field must
be resolved at compile time to a copy of the compile-time constant
 value, so uses of such a field are never active uses. See §13.4.8
for a further discussion. 
[...]


It seems that it is duty of compiler to not emit such calls. I wonder
what will happen if getstatic will be called anyway (by creating method
by hand). From JLS I understand it is illegal code and should be
verified away ?? Quite implementation dependent I'm afraid.

Artur



Re: Another reflection bug; compatibility results

2000-06-14 Thread Stuart Ballard


Artur Biesiadowski wrote:
 
 Stuart Ballard wrote:
 
  However, I think that asking for the value of a field, even a public
  static final one, is enough to cause clinit to get called... right? :(
 
 It depends if it is compile time constant. If yes, then clinit will not
 be called. From JLS 12.4.1
 
snipped
 
 It seems that it is duty of compiler to not emit such calls. I wonder
 what will happen if getstatic will be called anyway (by creating method
 by hand). From JLS I understand it is illegal code and should be
 verified away ?? Quite implementation dependent I'm afraid.

Unfortunately, I'm not generating the code by hand, but using
Reflection; reflection hadn't even been written when the JLS was created
and the reflection javadocs are pretty lame. As far as I can tell the
JLS has never been adequately updated to cover it. The reflection
specification from the 1.1 docs (at
http://java.sun.com/products/jdk/1.1/docs/guide/reflection/) is just a
copy of the javadocs in a different format, with some other basic
information about security models and things but nothing about the
obscure situations that come up, like this one.

What I really want is some sort of reflective call that will *tell* me
if a field is a compile-time constant (well, technically a primitive
constant, which additionally requires that it be static and final) and
allow me to get its value *if it is* without calling clinit. There's
clearly no such method in the standard reflection classes :( It would be
easy to add one to Kaffe, but I want to be writing portable Java code...

I may have to resort to one of these bytecode-interpretation libraries
eventually :(

Stuart.



Re: Another reflection bug; compatibility results

2000-06-14 Thread Godmar Back


 
 I worked around this by testing whether
 mth.equals(cls.getMethod(mth.getName(), mth.getParameterTypes())). This
 seems to expose another bug because on numerous occasions I get a
 NoSuchMethodException from this check, which shouldn't ever be
 possible... the imaginary conversation between my code and the class
 goes something like this:
 
 me: Hi, what methods do you support?
 class: I support a method called foo with parameter types {bar, baz}.
 me: Okay, give me the method called foo with parameter types {bar, baz}.
 class: I have no such method!
 
 I'll see if I can produce a simplified test case for both these bugs.

For the second bug, that would be nice.

I checked in a fix for the first problem (reporting overridden methods twice.)
Update libraries/clib/native/Class.c

- Godmar




Re: Another reflection bug; compatibility results

2000-06-14 Thread Godmar Back



 Have you tried calling 1.2's Class.forName(,false,) and then
using getField().getValue() on a final static field?
Does doing this invoke clinit?

If not, we should be able to fix Kaffe accordingly; I bet 10:1
it will call clinit.

But, actually, I don't really understand what that should be
good for anyway.  The only reason that accessing a final/static
field is not an active use is so the compiler can inline these
constants.

 
 I may have to resort to one of these bytecode-interpretation libraries
 eventually :(
 

I very much recommend it and I recommend JavaClass in particular.
Extremely easy to use - all types are named exactly as in the VM spec.
You can pretty much code without looking at the API.

- Godmar