1 /*
   2  * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package test.sun.invoke.util;
  25 
  26 import sun.invoke.util.ValueConversions;
  27 import sun.invoke.util.Wrapper;
  28 
  29 import java.lang.invoke.MethodType;
  30 import java.lang.invoke.MethodHandle;
  31 import java.io.Serializable;
  32 import java.util.Arrays;
  33 import org.junit.Test;
  34 import static org.junit.Assert.*;
  35 
  36 /* @test
  37  * @summary unit tests for value-type conversion utilities
  38  * @compile -XDignore.symbol.file ValueConversionsTest.java
  39  * @run junit/othervm test.sun.invoke.util.ValueConversionsTest
  40  */
  41 
  42 /**
  43  *
  44  * @author jrose
  45  */
  46 public class ValueConversionsTest {
  47     @Test
  48     public void testUnbox() throws Throwable {
  49         testUnbox(false);
  50     }
  51 
  52     @Test
  53     public void testUnboxCast() throws Throwable {
  54         testUnbox(true);
  55     }
  56 
  57     private void testUnbox(boolean doCast) throws Throwable {
  58         for (Wrapper dst : Wrapper.values()) {
  59             for (Wrapper src : Wrapper.values()) {
  60                 testUnbox(doCast, dst, src);
  61             }
  62         }
  63     }
  64 
  65     private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
  66         boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
  67         if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT)  return;  // must have prims
  68         if (dst == Wrapper.OBJECT)
  69             expectThrow = false;  // everything (even VOID==null here) converts to OBJECT
  70         try {
  71             for (int n = -5; n < 10; n++) {
  72                 Object box = src.wrap(n);
  73                 switch (src) {
  74                     case VOID:   assertEquals(box, null); break;
  75                     case OBJECT: box = box.toString(); break;
  76                     case SHORT:  assertEquals(box.getClass(), Short.class); break;
  77                     default:     assertEquals(box.getClass(), src.wrapperType()); break;
  78                 }
  79                 MethodHandle unboxer;
  80                 if (doCast)
  81                     unboxer = ValueConversions.unboxCast(dst.primitiveType());
  82                 else
  83                     unboxer = ValueConversions.unbox(dst.primitiveType());
  84                 Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
  85                 Object result = null;
  86                 switch (dst) {
  87                     case INT:     result = (int)     unboxer.invokeExact(box); break;
  88                     case LONG:    result = (long)    unboxer.invokeExact(box); break;
  89                     case FLOAT:   result = (float)   unboxer.invokeExact(box); break;
  90                     case DOUBLE:  result = (double)  unboxer.invokeExact(box); break;
  91                     case CHAR:    result = (char)    unboxer.invokeExact(box); break;
  92                     case BYTE:    result = (byte)    unboxer.invokeExact(box); break;
  93                     case SHORT:   result = (short)   unboxer.invokeExact(box); break;
  94                     case OBJECT:  result = (Object)  unboxer.invokeExact(box); break;
  95                     case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
  96                     case VOID:    result = null;     unboxer.invokeExact(box); break;
  97                 }
  98                 if (expectThrow) {
  99                     expResult = "(need an exception)";
 100                 }
 101                 assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box),
 102                              expResult, result);
 103             }
 104         } catch (RuntimeException ex) {
 105             if (expectThrow)  return;
 106             System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src));
 107             throw ex;
 108         }
 109     }
 110 
 111     @Test
 112     public void testBox() throws Throwable {
 113         for (Wrapper w : Wrapper.values()) {
 114             if (w == Wrapper.VOID)  continue;  // skip this; no unboxed form
 115             for (int n = -5; n < 10; n++) {
 116                 Object box = w.wrap(n);
 117                 MethodHandle boxer = ValueConversions.box(w.primitiveType());
 118                 Object expResult = box;
 119                 Object result = null;
 120                 switch (w) {
 121                     case INT:     result = boxer.invokeExact(/*int*/n); break;
 122                     case LONG:    result = boxer.invokeExact((long)n); break;
 123                     case FLOAT:   result = boxer.invokeExact((float)n); break;
 124                     case DOUBLE:  result = boxer.invokeExact((double)n); break;
 125                     case CHAR:    result = boxer.invokeExact((char)n); break;
 126                     case BYTE:    result = boxer.invokeExact((byte)n); break;
 127                     case SHORT:   result = boxer.invokeExact((short)n); break;
 128                     case OBJECT:  result = boxer.invokeExact((Object)n); break;
 129                     case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
 130                 }
 131                 assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
 132                              expResult, result);
 133             }
 134         }
 135     }
 136 
 137     @Test
 138     public void testCast() throws Throwable {
 139         Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
 140         Object[] objects = { new Object(), Boolean.FALSE,      "hello",      (Long)12L,    (Integer)6    };
 141         for (Class<?> dst : types) {
 142             MethodHandle caster = ValueConversions.cast(dst);
 143             assertEquals(caster.type(), ValueConversions.identity().type());
 144             for (Object obj : objects) {
 145                 Class<?> src = obj.getClass();
 146                 boolean canCast = dst.isAssignableFrom(src);
 147                 try {
 148                     Object result = caster.invokeExact(obj);
 149                     if (canCast)
 150                         assertEquals(obj, result);
 151                     else
 152                         assertEquals("cast should not have succeeded", dst, obj);
 153                 } catch (ClassCastException ex) {
 154                     if (canCast)
 155                         throw ex;
 156                 }
 157             }
 158         }
 159     }
 160 
 161     @Test
 162     public void testIdentity() throws Throwable {
 163         MethodHandle id = ValueConversions.identity();
 164         Object expResult = "foo";
 165         Object result = id.invokeExact(expResult);
 166         assertEquals(expResult, result);
 167     }
 168 
 169     @Test
 170     public void testConvert() throws Throwable {
 171         for (long tval = 0, ctr = 0;;) {
 172             if (++ctr > 99999)  throw new AssertionError("too many test values");
 173             // prints 3776 test patterns (3776 = 8*59*8)
 174             tval = nextTestValue(tval);
 175             if (tval == 0) {
 176                 break;  // repeat
 177             }
 178         }
 179         for (Wrapper src : Wrapper.values()) {
 180             for (Wrapper dst : Wrapper.values()) {
 181                 testConvert(src, dst, 0);
 182             }
 183         }
 184     }
 185     static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable {
 186         boolean testSingleCase = (tval != 0);
 187         final long tvalInit = tval;
 188         MethodHandle conv = ValueConversions.convertPrimitive(src, dst);
 189         MethodType convType;
 190         if (src == Wrapper.VOID)
 191             convType = MethodType.methodType(dst.primitiveType() /* , void */);
 192         else
 193             convType = MethodType.methodType(dst.primitiveType(), src.primitiveType());
 194         assertEquals(convType, conv.type());
 195         MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class));
 196         for (;;) {
 197             long n = tval;
 198             Object testValue = src.wrap(n);
 199             Object expResult = dst.cast(testValue, dst.primitiveType());
 200             Object result;
 201             switch (src) {
 202                 case INT:     result = converter.invokeExact((int)n); break;
 203                 case LONG:    result = converter.invokeExact(/*long*/n); break;
 204                 case FLOAT:   result = converter.invokeExact((float)n); break;
 205                 case DOUBLE:  result = converter.invokeExact((double)n); break;
 206                 case CHAR:    result = converter.invokeExact((char)n); break;
 207                 case BYTE:    result = converter.invokeExact((byte)n); break;
 208                 case SHORT:   result = converter.invokeExact((short)n); break;
 209                 case OBJECT:  result = converter.invokeExact((Object)n); break;
 210                 case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break;
 211                 case VOID:    result = converter.invokeExact(); break;
 212                 default:  throw new AssertionError();
 213             }
 214             assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue),
 215                          expResult, result);
 216             if (testSingleCase)  break;
 217             // next test value:
 218             tval = nextTestValue(tval);
 219             if (tval == tvalInit)  break;  // repeat
 220         }
 221     }
 222     static long tweakSign(long x) {
 223         // Assuming that x is mostly zeroes, make those zeroes follow bit #62 (just below the sign).
 224         // This function is self-inverse.
 225         final long MID_SIGN_BIT = 62;
 226         long sign = -((x >>> MID_SIGN_BIT) & 1);  // all ones or all zeroes
 227         long flip = (sign >>> -MID_SIGN_BIT);  // apply the sign below the mid-bit
 228         return x ^ flip;
 229     }
 230     static long nextTestValue(long x) {
 231         // Produce 64 bits with three component bitfields:  [ high:3 | mid:58 | low:3 ].
 232         // The high and low fields vary through all possible bit patterns.
 233         // The middle field is either all zero or has a single bit set.
 234         // For better coverage of the neighborhood of zero, an internal sign bit is xored downward also.
 235         long ux = tweakSign(x);  // unsign the middle field
 236         final long LOW_BITS  = 3, LOW_BITS_MASK  = (1L << LOW_BITS)-1;
 237         final long HIGH_BITS = 3, HIGH_BITS_MASK = ~(-1L >>> HIGH_BITS);
 238         if ((ux & LOW_BITS_MASK) != LOW_BITS_MASK) {
 239             ++ux;
 240         } else {
 241             ux &= ~LOW_BITS_MASK;
 242             long midBit = (ux & ~HIGH_BITS_MASK);
 243             if (midBit == 0)
 244                 midBit = (1L<<LOW_BITS);  // introduce a low bit
 245             ux += midBit;
 246         }
 247         return tweakSign(ux);
 248     }
 249 }