[Kaffe] found cause of bug in boolean method invocation, help needed.

Moses DeJong dejong at cs.umn.edu
Sun Feb 7 18:02:53 PST 1999


Hi all.


I think I have figured out why invocations of boolean methods
using reflection do not work on my sparc solaris box. It looks
like there is a endian problem with the way boolean values are
transfered from the stack to the result to be returned. Here
is an example of the problem and a bit more info.


import java.lang.reflect.*;

public class ReflectedBooleanBug4 {
    public static void main(String[] argv) throws Exception {
	Method m1 = ReflectedBooleanBug4.class.getMethod("trueBooleanObjectMethod", null);
	System.out.println("m1 is " + m1);

	Method m2 = ReflectedBooleanBug4.class.getMethod("falseBooleanObjectMethod", null);
	System.out.println("m2 is " + m2);


	Boolean tb = (Boolean) m1.invoke(null,null);
	Boolean fb = (Boolean) m2.invoke(null,null);

	if (tb.booleanValue() != true) {
	    throw new Exception("booleanValue() != true");
	}

	if (fb.booleanValue() != false) {
	    throw new Exception("booleanValue() != false");
	}

	System.out.println("OK");
    }

    public static boolean trueBooleanObjectMethod() {
	return true;
    }

    public static boolean falseBooleanObjectMethod() {
	return false;
    }
}

/*

% kaffe ReflectedBooleanBug4
m1 is public static boolean ReflectedBooleanBug4.trueBooleanObjectMethod()
m2 is public static boolean ReflectedBooleanBug4.falseBooleanObjectMethod()
java.lang.Exception: booleanValue() != true
        at java.lang.Throwable.<init>(Throwable.java:37)
        at java.lang.Exception.<init>(Exception.java:21)
        at ReflectedBooleanBug4.main(ReflectedBooleanBug4.java:17)

*/

/*
JDK
% java ReflectedBooleanBug4
m1 is public static boolean ReflectedBooleanBug4.trueBooleanObjectMethod()
m2 is public static boolean ReflectedBooleanBug4.falseBooleanObjectMethod()
OK

*/




Let's start out in file jni.c in the function Kaffe_CallStaticBooleanMethodA().
In that method a jvalue is created on the C stack and a pointer to this jvalue
is passed to callMethodA() which is then passed to virtualMachine() (I am
using a non JIT build in this example). Inside virtualMachine() in the file
machine.c, the java byte codes are interpreted. For the example file given
above the method trueBooleanObjectMethod() is defined in java byte code like
so. It pushes an integer 1 onto the stack and then returns.


Method boolean trueBooleanObjectMethod()
   0 iconst_1
   1 ireturn


The return bytecode is defined like this inside kaffe.def. 


define_insn(IRETURN)
{
        check_stack_int(0);

        monitor_exit();
        returnarg_int(rstack(0));
        end_function();
        ret();
}

The only to operation that matters here is returnarg_int().
It is defined like so inside icode.h.


#define returnarg_int(s) \
                retval[0].v.tint = (s)[0].v.tint


In this case both retval[0] and (s)[0] (the value on the stack)
are of type "slots" as defined in file constants.h. This assignment
changes the value of the jvalue argument I mentioned earlier (the
jvalue argument has been cast to a slots* and it is stored in retval).


The union jvalue type has a jboolean member but it is only one byte
in size so the assignment of 1 to the tint member of the slots union
will not change the value of the boolean on a bigendian machine.
This does work on a little endian machine like an intel box.
I have included a small C program and it's output which shows
what is going on on big vs little endian systems.



Intel (little endian) box with gcc + linux.

% ./sizes 
ref[0].v.tint is "1"
var.z is "1"


Sparc (big endian) box with gcc + Solaris

% ./sizes 
ref[0].v.tint is "1"
var.z is 0




/* start file sizes.c */


#include <stdio.h>


union jvalue {
  char          z;
  char		b;
  char		c; 
  short		s;
  int		i;
  long		j;
  float		f;
  double        d;
  void*		l;
};

typedef union jvalue jvalue;


struct _slots {
        union {
                int            tint;
                long           tword;
                long           tlong;
                float          tfloat;
                double         tdouble;
                void*           taddr;
                char*           tstr;
        } v;
};

typedef struct _slots slots;



void main(int argc, char ** argv) {
  jvalue var;
  slots val;
  slots* ref;


  var.l = 0;
  val.v.tint = 1;
  ref = (slots *) &var;

  ref[0].v.tint =  val.v.tint;

  if (ref[0].v.tint) {
    printf("ref[0].v.tint is \"%d\"\n", ref[0].v.tint);
  } else {
    printf("ref[0].v.tint is 0\n");
  }

  if (var.z) {
    printf("var.z is \"%d\"\n", ((int) var.z));
  } else {
    printf("var.z is 0\n");
  }
}


/* end file sizes.c */






I am not sure what the "right" way to fix this is. Does someone that
knows a little more about the Kaffe internals comment about the best
way to fix this for little and big endian machines?



Mo DeJong
dejong at cs.umn.edu



More information about the kaffe mailing list