1 /*
   2  * Copyright (c) 200, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2015, Red Hat Inc. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 /**
  27  * @test
  28  * @bug 8026049
  29  * @summary Verify that byte buffers are correctly accessed.
  30  * @modules java.base/jdk.internal.misc
  31  * @library /testlibrary
  32  *
  33  * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-UseUnalignedAccesses -Djdk.test.lib.random.seed=0
  34  *      compiler.intrinsics.unsafe.HeapByteBufferTest
  35  * @run main/othervm -Djdk.test.lib.random.seed=0
  36  *      compiler.intrinsics.unsafe.HeapByteBufferTest
  37  */
  38 
  39 package compiler.intrinsics.unsafe;
  40 
  41 import jdk.test.lib.Utils;
  42 
  43 import java.nio.BufferOverflowException;
  44 import java.nio.BufferUnderflowException;
  45 import java.nio.ByteBuffer;
  46 import java.nio.ByteOrder;
  47 import java.util.Arrays;
  48 import java.util.Random;
  49 
  50 import static java.nio.ByteOrder.BIG_ENDIAN;
  51 import static java.nio.ByteOrder.LITTLE_ENDIAN;
  52 
  53 // A wrapper for a ByteBuffer which maintains a backing array and a
  54 // position.  Whenever this wrapper is written the backing array and
  55 // the wrapped byte buffer are updated together, and whenever it is
  56 // read we check that the ByteBuffer and the backing array are identical.
  57 
  58 public class HeapByteBufferTest implements Runnable {
  59     static class MyByteBuffer {
  60         final ByteBuffer buf;
  61         final byte[] bytes;
  62         int pos;
  63         ByteOrder byteOrder = BIG_ENDIAN;
  64 
  65         MyByteBuffer(ByteBuffer buf, byte[] bytes) {
  66             this.buf = buf;
  67             this.bytes = Arrays.copyOf(bytes, bytes.length);
  68             pos = 0;
  69         }
  70 
  71         public final MyByteBuffer order(ByteOrder bo) {
  72             byteOrder = bo;
  73             buf.order(bo);
  74             return this;
  75         }
  76 
  77         static MyByteBuffer wrap(byte[] bytes) {
  78             return new MyByteBuffer(ByteBuffer.wrap(bytes), bytes);
  79         }
  80 
  81         int capacity() { return bytes.length; }
  82         int position() {
  83             if (buf.position() != pos)
  84                 throw new RuntimeException();
  85             return buf.position();
  86         }
  87 
  88         byte[] array() { return buf.array(); }
  89         byte[] backingArray() { return bytes; }
  90 
  91         private static byte long7(long x) { return (byte)(x >> 56); }
  92         private static byte long6(long x) { return (byte)(x >> 48); }
  93         private static byte long5(long x) { return (byte)(x >> 40); }
  94         private static byte long4(long x) { return (byte)(x >> 32); }
  95         private static byte long3(long x) { return (byte)(x >> 24); }
  96         private static byte long2(long x) { return (byte)(x >> 16); }
  97         private static byte long1(long x) { return (byte)(x >>  8); }
  98         private static byte long0(long x) { return (byte)(x      ); }
  99 
 100         private static byte int3(int x) { return (byte)(x >> 24); }
 101         private static byte int2(int x) { return (byte)(x >> 16); }
 102         private static byte int1(int x) { return (byte)(x >>  8); }
 103         private static byte int0(int x) { return (byte)(x      ); }
 104 
 105         private static byte short1(short x) { return (byte)(x >> 8); }
 106         private static byte short0(short x) { return (byte)(x     ); }
 107 
 108         byte _get(long i) { return bytes[(int)i]; }
 109         void _put(long i, byte x) { bytes[(int)i] = x; }
 110 
 111         private void putLongX(long a, long x) {
 112             if (byteOrder == BIG_ENDIAN) {
 113                 x = Long.reverseBytes(x);
 114             }
 115             _put(a + 7, long7(x));
 116             _put(a + 6, long6(x));
 117             _put(a + 5, long5(x));
 118             _put(a + 4, long4(x));
 119             _put(a + 3, long3(x));
 120             _put(a + 2, long2(x));
 121             _put(a + 1, long1(x));
 122             _put(a    , long0(x));
 123         }
 124 
 125         private void putIntX(long a, int x) {
 126             if (byteOrder == BIG_ENDIAN) {
 127                 x = Integer.reverseBytes(x);
 128             }
 129             _put(a + 3, int3(x));
 130             _put(a + 2, int2(x));
 131             _put(a + 1, int1(x));
 132             _put(a    , int0(x));
 133         }
 134 
 135         private void putShortX(int bi, short x) {
 136             if (byteOrder == BIG_ENDIAN) {
 137                 x = Short.reverseBytes(x);
 138             }
 139             _put(bi    , short0(x));
 140             _put(bi + 1, short1(x));
 141         }
 142 
 143         static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
 144             return (((b3       ) << 24) |
 145                     ((b2 & 0xff) << 16) |
 146                     ((b1 & 0xff) <<  8) |
 147                     ((b0 & 0xff)      ));
 148         }
 149         int getIntX(long a) {
 150             int x = makeInt(_get(a + 3),
 151                     _get(a + 2),
 152                     _get(a + 1),
 153                     _get(a));
 154             if (byteOrder == BIG_ENDIAN) {
 155                 x = Integer.reverseBytes(x);
 156             }
 157             return x;
 158         }
 159 
 160         static private long makeLong(byte b7, byte b6, byte b5, byte b4,
 161                                      byte b3, byte b2, byte b1, byte b0)
 162         {
 163             return ((((long)b7       ) << 56) |
 164                     (((long)b6 & 0xff) << 48) |
 165                     (((long)b5 & 0xff) << 40) |
 166                     (((long)b4 & 0xff) << 32) |
 167                     (((long)b3 & 0xff) << 24) |
 168                     (((long)b2 & 0xff) << 16) |
 169                     (((long)b1 & 0xff) <<  8) |
 170                     (((long)b0 & 0xff)      ));
 171         }
 172 
 173         long getLongX(long a) {
 174             long x = makeLong(_get(a + 7),
 175                     _get(a + 6),
 176                     _get(a + 5),
 177                     _get(a + 4),
 178                     _get(a + 3),
 179                     _get(a + 2),
 180                     _get(a + 1),
 181                     _get(a));
 182             if (byteOrder == BIG_ENDIAN) {
 183                 x = Long.reverseBytes(x);
 184             }
 185             return x;
 186         }
 187 
 188         static private short makeShort(byte b1, byte b0) {
 189             return (short)((b1 << 8) | (b0 & 0xff));
 190         }
 191 
 192         short getShortX(long a) {
 193             short x = makeShort(_get(a + 1),
 194                     _get(a    ));
 195             if (byteOrder == BIG_ENDIAN) {
 196                 x = Short.reverseBytes(x);
 197             }
 198             return x;
 199         }
 200 
 201         double getDoubleX(long a) {
 202             long x = getLongX(a);
 203             return Double.longBitsToDouble(x);
 204         }
 205 
 206         double getFloatX(long a) {
 207             int x = getIntX(a);
 208             return Float.intBitsToFloat(x);
 209         }
 210 
 211         void ck(long x, long y) {
 212             if (x != y) {
 213                 throw new RuntimeException(" x = " + Long.toHexString(x) + ", y = " + Long.toHexString(y));
 214             }
 215         }
 216 
 217         void ck(double x, double y) {
 218             if (x == x && y == y && x != y) {
 219                 ck(x, y);
 220             }
 221         }
 222 
 223         long getLong(int i) { ck(buf.getLong(i), getLongX(i)); return buf.getLong(i); }
 224         int getInt(int i) { ck(buf.getInt(i), getIntX(i)); return buf.getInt(i); }
 225         short getShort(int i) { ck(buf.getShort(i), getShortX(i)); return buf.getShort(i); }
 226         char getChar(int i) { ck(buf.getChar(i), (char)getShortX(i)); return buf.getChar(i); }
 227         double getDouble(int i) { ck(buf.getDouble(i), getDoubleX(i)); return buf.getDouble(i); }
 228         float getFloat(int i) { ck(buf.getFloat(i), getFloatX(i)); return buf.getFloat(i); }
 229 
 230         void putLong(int i, long x) { buf.putLong(i, x); putLongX(i, x); }
 231         void putInt(int i, int x) { buf.putInt(i, x); putIntX(i, x); }
 232         void putShort(int i, short x) { buf.putShort(i, x); putShortX(i, x); }
 233         void putChar(int i, char x) { buf.putChar(i, x); putShortX(i, (short)x); }
 234         void putDouble(int i, double x) { buf.putDouble(i, x); putLongX(i, Double.doubleToRawLongBits(x)); }
 235         void putFloat(int i, float x) { buf.putFloat(i, x); putIntX(i, Float.floatToRawIntBits(x)); }
 236 
 237         long getLong() { ck(buf.getLong(buf.position()), getLongX(pos)); long x = buf.getLong(); pos += 8; return x; }
 238         int getInt() { ck(buf.getInt(buf.position()), getIntX(pos)); int x = buf.getInt(); pos += 4; return x; }
 239         short getShort() { ck(buf.getShort(buf.position()), getShortX(pos)); short x = buf.getShort(); pos += 2; return x; }
 240         char getChar() {  ck(buf.getChar(buf.position()), (char)getShortX(pos)); char x = buf.getChar(); pos += 2; return x; }
 241         double getDouble() { ck(buf.getDouble(buf.position()), getDoubleX(pos)); double x = buf.getDouble(); pos += 8; return x; }
 242         float getFloat() { ck(buf.getFloat(buf.position()), getFloatX(pos)); float x = buf.getFloat(); pos += 4; return x; }
 243 
 244         void putLong(long x) { putLongX(pos, x); pos += 8; buf.putLong(x); }
 245         void putInt(int x) { putIntX(pos, x); pos += 4; buf.putInt(x); }
 246         void putShort(short x) { putShortX(pos, x); pos += 2; buf.putShort(x); }
 247         void putChar(char x) { putShortX(pos, (short)x); pos += 2; buf.putChar(x); }
 248         void putDouble(double x) { putLongX(pos, Double.doubleToRawLongBits(x)); pos += 8; buf.putDouble(x); }
 249         void putFloat(float x) { putIntX(pos, Float.floatToRawIntBits(x)); pos += 4; buf.putFloat(x); }
 250 
 251         void rewind() { pos = 0; buf.rewind(); }
 252     }
 253 
 254     Random random = Utils.getRandomInstance();
 255     MyByteBuffer data = MyByteBuffer.wrap(new byte[1024]);
 256 
 257     int randomOffset(Random r, MyByteBuffer buf, int size) {
 258         return r.nextInt(buf.capacity() - size);
 259     }
 260 
 261     long iterations;
 262 
 263     HeapByteBufferTest(long iterations) {
 264         this.iterations = iterations;
 265     }
 266 
 267     // The core of the test.  Walk over the buffer reading and writing
 268     // random data, XORing it as we go.  We can detect writes in the
 269     // wrong place, writes which are too long or too short, and reads
 270     // or writes of the wrong data,
 271     void step(Random r) {
 272         data.order((r.nextInt() & 1) != 0 ? BIG_ENDIAN : LITTLE_ENDIAN);
 273 
 274         data.rewind();
 275         while (data.position() < data.capacity())
 276             data.putLong(data.getLong() ^ random.nextLong());
 277 
 278         data.rewind();
 279         while (data.position() < data.capacity())
 280             data.putInt(data.getInt() ^ random.nextInt());
 281 
 282         data.rewind();
 283         while (data.position() < data.capacity())
 284             data.putShort((short)(data.getShort() ^ random.nextInt()));
 285 
 286         data.rewind();
 287         while (data.position() < data.capacity())
 288             data.putChar((char)(data.getChar() ^ random.nextInt()));
 289 
 290         data.rewind();
 291         while (data.position() < data.capacity()) {
 292             data.putDouble(combine(data.getDouble(), random.nextLong()));
 293         }
 294 
 295         data.rewind();
 296         while (data.position() < data.capacity())
 297             data.putFloat(combine(data.getFloat(), random.nextInt()));
 298 
 299         for (int i = 0; i < 100; i++) {
 300             int offset = randomOffset(r, data, 8);
 301             data.putLong(offset, data.getLong(offset) ^ random.nextLong());
 302         }
 303         for (int i = 0; i < 100; i++) {
 304             int offset = randomOffset(r, data, 4);
 305             data.putInt(offset, data.getInt(offset) ^ random.nextInt());
 306         }
 307         for (int i = 0; i < 100; i++) {
 308             int offset = randomOffset(r, data, 2);
 309             data.putShort(offset, (short)(data.getShort(offset) ^ random.nextInt()));
 310         }
 311         for (int i = 0; i < 100; i++) {
 312             int offset = randomOffset(r, data, 2);
 313             data.putChar(offset, (char)(data.getChar(offset) ^ random.nextInt()));
 314         }
 315         for (int i = 0; i < 100; i++) {
 316             int offset = randomOffset(r, data, 8);
 317             data.putDouble(offset, combine(data.getDouble(offset), random.nextLong()));
 318         }
 319         for (int i = 0; i < 100; i++) {
 320             int offset = randomOffset(r, data, 4);
 321             data.putFloat(offset, combine(data.getFloat(offset), random.nextInt()));
 322         }
 323     }
 324 
 325     // XOR the bit pattern of a double and a long, returning the
 326     // result as a double.
 327     //
 328     // We convert signalling NaNs to quiet NaNs.  We need to do this
 329     // because some platforms (in particular legacy 80x87) do not
 330     // provide transparent conversions between integer and
 331     // floating-point types even when using raw conversions but
 332     // quietly convert sNaN to qNaN.  This causes spurious test
 333     // failures when the template interpreter uses 80x87 and the JITs
 334     // use XMM registers.
 335     //
 336     public double combine(double prev, long bits) {
 337         bits ^= Double.doubleToRawLongBits(prev);
 338         double result = Double.longBitsToDouble(bits);
 339         if (Double.isNaN(result)) {
 340             result = Double.longBitsToDouble(bits | 0x8000000000000l);
 341         }
 342         return result;
 343     }
 344 
 345     // XOR the bit pattern of a float and an int, returning the result
 346     // as a float.  Convert sNaNs to qNaNs.
 347     public Float combine(float prev, int bits) {
 348         bits ^= Float.floatToRawIntBits(prev);
 349         Float result = Float.intBitsToFloat(bits);
 350         if (Float.isNaN(result)) {
 351             result = Float.intBitsToFloat(bits | 0x400000);
 352         }
 353         return result;
 354     }
 355 
 356     enum PrimitiveType {
 357         BYTE(1), CHAR(2), SHORT(2), INT(4), LONG(8), FLOAT(4), DOUBLE(8);
 358 
 359         public final int size;
 360         PrimitiveType(int size) {
 361             this.size = size;
 362         }
 363     }
 364 
 365     void getOne(ByteBuffer b, PrimitiveType t) {
 366         switch (t) {
 367         case BYTE: b.get(); break;
 368         case CHAR: b.getChar(); break;
 369         case SHORT: b.getShort(); break;
 370         case INT: b.getInt(); break;
 371         case LONG: b.getLong(); break;
 372         case FLOAT: b.getFloat(); break;
 373         case DOUBLE: b.getDouble(); break;
 374         }
 375     }
 376 
 377     void putOne(ByteBuffer b, PrimitiveType t) {
 378         switch (t) {
 379         case BYTE: b.put((byte)0); break;
 380         case CHAR: b.putChar('0'); break;
 381         case SHORT: b.putShort((short)0); break;
 382         case INT: b.putInt(0); break;
 383         case LONG: b.putLong(0); break;
 384         case FLOAT: b.putFloat(0); break;
 385         case DOUBLE: b.putDouble(0); break;
 386         }
 387     }
 388 
 389     void getOne(ByteBuffer b, PrimitiveType t, int index) {
 390         switch (t) {
 391         case BYTE: b.get(index); break;
 392         case CHAR: b.getChar(index); break;
 393         case SHORT: b.getShort(index); break;
 394         case INT: b.getInt(index); break;
 395         case LONG: b.getLong(index); break;
 396         case FLOAT: b.getFloat(index); break;
 397         case DOUBLE: b.getDouble(index); break;
 398         }
 399     }
 400 
 401     void putOne(ByteBuffer b, PrimitiveType t, int index) {
 402         switch (t) {
 403         case BYTE: b.put(index, (byte)0); break;
 404         case CHAR: b.putChar(index, '0'); break;
 405         case SHORT: b.putShort(index, (short)0); break;
 406         case INT: b.putInt(index, 0); break;
 407         case LONG: b.putLong(index, 0); break;
 408         case FLOAT: b.putFloat(index, 0); break;
 409         case DOUBLE: b.putDouble(index, 0); break;
 410         }
 411     }
 412 
 413     void checkBoundaryConditions() {
 414         for (int i = 0; i < 100; i++) {
 415             int bufSize = random.nextInt(16);
 416             byte[] bytes = new byte[bufSize];
 417             ByteBuffer buf = ByteBuffer.wrap(bytes);
 418             for (int j = 0; j < 100; j++) {
 419                 int offset = random.nextInt(32) - 8;
 420                 for (PrimitiveType t : PrimitiveType.values()) {
 421                     int threw = 0;
 422                     try {
 423                         try {
 424                             buf.position(offset);
 425                             getOne(buf, t);
 426                         } catch (BufferUnderflowException e) {
 427                             if (offset + t.size < bufSize)
 428                                 throw new RuntimeException
 429                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 430                             threw++;
 431                         } catch (IllegalArgumentException e) {
 432                             if (offset >= 0 && offset + t.size < bufSize)
 433                                 throw new RuntimeException
 434                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 435                             threw++;
 436                         }
 437 
 438                         try {
 439                             buf.position(offset);
 440                             putOne(buf, t);
 441                         } catch (BufferOverflowException e) {
 442                             if (offset + t.size < bufSize)
 443                                 throw new RuntimeException
 444                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 445                             threw++;
 446                         } catch (IllegalArgumentException e) {
 447                             if (offset >= 0 && offset + t.size < bufSize)
 448                                 throw new RuntimeException
 449                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 450                             threw++;
 451                         }
 452 
 453                         try {
 454                             putOne(buf, t, offset);
 455                         } catch (IndexOutOfBoundsException e) {
 456                             if (offset >= 0 && offset + t.size < bufSize)
 457                                 throw new RuntimeException
 458                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 459                             threw++;
 460                         }
 461 
 462                         try {
 463                             getOne(buf, t, offset);
 464                         } catch (IndexOutOfBoundsException e) {
 465                             if (offset >= 0 && offset + t.size < bufSize)
 466                                 throw new RuntimeException
 467                                     ("type = " + t + ", offset = " + offset + ", bufSize = " + bufSize, e);
 468                             threw++;
 469                         }
 470 
 471                         if (threw == 0) {
 472                             // Make sure that we should not have thrown.
 473                             if (offset < 0 || offset + t.size > bufSize) {
 474                                 throw new RuntimeException
 475                                     ("should have thrown but did not, type = " + t
 476                                      + ", offset = " + offset + ", bufSize = " + bufSize);
 477                             }
 478                         } else if (threw != 4) {
 479                             // If one of the {get,put} operations threw
 480                             // due to an invalid offset then all four of
 481                             // them should have thrown.
 482                             throw new RuntimeException
 483                                 ("should have thrown but at least one did not, type = " + t
 484                                  + ", offset = " + offset + ", bufSize = " + bufSize);
 485                         }
 486                     } catch (Throwable th) {
 487                         throw new RuntimeException
 488                             ("unexpected throw: type  = " + t + ", offset = " + offset + ", bufSize = " + bufSize, th);
 489 
 490                     }
 491                 }
 492             }
 493         }
 494     }
 495 
 496     public void run() {
 497         checkBoundaryConditions();
 498 
 499         for (int i = 0; i < data.capacity(); i += 8) {
 500             data.putLong(i, random.nextLong());
 501         }
 502 
 503         for (int i = 0; i < iterations; i++) {
 504             step(random);
 505         }
 506 
 507         if (!Arrays.equals(data.array(), data.backingArray())) {
 508             throw new RuntimeException();
 509         }
 510     }
 511 
 512     public static void main(String[] args) {
 513         // The number of iterations is high to ensure that tiered
 514         // compilation kicks in all the way up to C2.
 515         long iterations = 100000;
 516         if (args.length > 0)
 517             iterations = Long.parseLong(args[0]);
 518 
 519         new HeapByteBufferTest(iterations).run();
 520     }
 521 }