View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.Field;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.nio.Buffer;
27  import java.nio.ByteBuffer;
28  import java.security.AccessController;
29  import java.security.PrivilegedAction;
30  
31  import static io.netty.util.internal.ObjectUtil.checkNotNull;
32  
33  /**
34   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
35   */
36  @SuppressJava6Requirement(reason = "Unsafe access is guarded")
37  final class PlatformDependent0 {
38  
39      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
40      private static final long ADDRESS_FIELD_OFFSET;
41      private static final long BYTE_ARRAY_BASE_OFFSET;
42      private static final long INT_ARRAY_BASE_OFFSET;
43      private static final long INT_ARRAY_INDEX_SCALE;
44      private static final long LONG_ARRAY_BASE_OFFSET;
45      private static final long LONG_ARRAY_INDEX_SCALE;
46      private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
47      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
48      private static final Method ALLOCATE_ARRAY_METHOD;
49      private static final Method ALIGN_SLICE;
50      private static final int JAVA_VERSION = javaVersion0();
51      private static final boolean IS_ANDROID = isAndroid0();
52      private static final boolean STORE_FENCE_AVAILABLE;
53  
54      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
55      private static final Object INTERNAL_UNSAFE;
56      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
57  
58      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
59      // ImageInfo.java
60      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
61              "org.graalvm.nativeimage.imagecode");
62  
63      static final Unsafe UNSAFE;
64  
65      // constants borrowed from murmur3
66      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
67      static final int HASH_CODE_C1 = 0xcc9e2d51;
68      static final int HASH_CODE_C2 = 0x1b873593;
69  
70      /**
71       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
72       * during a large copy.
73       */
74      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
75  
76      private static final boolean UNALIGNED;
77  
78      static {
79          final ByteBuffer direct;
80          Field addressField = null;
81          Method allocateArrayMethod = null;
82          Throwable unsafeUnavailabilityCause = null;
83          Unsafe unsafe;
84          Object internalUnsafe = null;
85          boolean storeFenceAvailable = false;
86          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
87              direct = null;
88              addressField = null;
89              unsafe = null;
90              internalUnsafe = null;
91          } else {
92              direct = ByteBuffer.allocateDirect(1);
93  
94              // attempt to access field Unsafe#theUnsafe
95              final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
96                  @Override
97                  public Object run() {
98                      try {
99                          final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
100                         // We always want to try using Unsafe as the access still works on java9 as well and
101                         // we need it for out native-transports and many optimizations.
102                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
103                         if (cause != null) {
104                             return cause;
105                         }
106                         // the unsafe instance
107                         return unsafeField.get(null);
108                     } catch (NoSuchFieldException e) {
109                         return e;
110                     } catch (SecurityException e) {
111                         return e;
112                     } catch (IllegalAccessException e) {
113                         return e;
114                     } catch (NoClassDefFoundError e) {
115                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
116                         // Unsafe unloadable.
117                         return e;
118                     }
119                 }
120             });
121 
122             // the conditional check here can not be replaced with checking that maybeUnsafe
123             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
124             // instanceof check against Unsafe will trigger a class load and we might not have
125             // the runtime permission accessClassInPackage.sun.misc
126             if (maybeUnsafe instanceof Throwable) {
127                 unsafe = null;
128                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
129                 if (logger.isTraceEnabled()) {
130                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
131                 } else {
132                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", ((Throwable) maybeUnsafe).getMessage());
133                 }
134             } else {
135                 unsafe = (Unsafe) maybeUnsafe;
136                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
137             }
138 
139             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK
140             // https://github.com/netty/netty/issues/1061
141             // https://www.mail-archive.com/[email protected]/msg00698.html
142             if (unsafe != null) {
143                 final Unsafe finalUnsafe = unsafe;
144                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
145                     @Override
146                     public Object run() {
147                         try {
148                             finalUnsafe.getClass().getDeclaredMethod(
149                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
150                             return null;
151                         } catch (NoSuchMethodException e) {
152                             return e;
153                         } catch (SecurityException e) {
154                             return e;
155                         }
156                     }
157                 });
158 
159                 if (maybeException == null) {
160                     logger.debug("sun.misc.Unsafe.copyMemory: available");
161                 } else {
162                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
163                     unsafe = null;
164                     unsafeUnavailabilityCause = (Throwable) maybeException;
165                     if (logger.isTraceEnabled()) {
166                         logger.debug("sun.misc.Unsafe.copyMemory: unavailable", (Throwable) maybeException);
167                     } else {
168                         logger.debug("sun.misc.Unsafe.copyMemory: unavailable: {}",
169                                 ((Throwable) maybeException).getMessage());
170                     }
171                 }
172             }
173 
174             // ensure Unsafe::storeFence to be available: jdk < 8 shouldn't have it
175             if (unsafe != null) {
176                 final Unsafe finalUnsafe = unsafe;
177                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
178                     @Override
179                     public Object run() {
180                         try {
181                             finalUnsafe.getClass().getDeclaredMethod("storeFence");
182                             return null;
183                         } catch (NoSuchMethodException e) {
184                             return e;
185                         } catch (SecurityException e) {
186                             return e;
187                         }
188                     }
189                 });
190 
191                 if (maybeException == null) {
192                     logger.debug("sun.misc.Unsafe.storeFence: available");
193                     storeFenceAvailable = true;
194                 } else {
195                     storeFenceAvailable = false;
196                     // Unsafe.storeFence unavailable.
197                     if (logger.isTraceEnabled()) {
198                         logger.debug("sun.misc.Unsafe.storeFence: unavailable", (Throwable) maybeException);
199                     } else {
200                         logger.debug("sun.misc.Unsafe.storeFence: unavailable: {}",
201                                      ((Throwable) maybeException).getMessage());
202                     }
203                 }
204             }
205 
206             if (unsafe != null) {
207                 final Unsafe finalUnsafe = unsafe;
208 
209                 // attempt to access field Buffer#address
210                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
211                     @Override
212                     public Object run() {
213                         try {
214                             final Field field = Buffer.class.getDeclaredField("address");
215                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
216                             // will forbid changing the access level via reflection.
217                             final long offset = finalUnsafe.objectFieldOffset(field);
218                             final long address = finalUnsafe.getLong(direct, offset);
219 
220                             // if direct really is a direct buffer, address will be non-zero
221                             if (address == 0) {
222                                 return null;
223                             }
224                             return field;
225                         } catch (NoSuchFieldException e) {
226                             return e;
227                         } catch (SecurityException e) {
228                             return e;
229                         }
230                     }
231                 });
232 
233                 if (maybeAddressField instanceof Field) {
234                     addressField = (Field) maybeAddressField;
235                     logger.debug("java.nio.Buffer.address: available");
236                 } else {
237                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
238                     if (logger.isTraceEnabled()) {
239                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
240                     } else {
241                         logger.debug("java.nio.Buffer.address: unavailable: {}",
242                                 ((Throwable) maybeAddressField).getMessage());
243                     }
244 
245                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
246                     // Let's just pretend unsafe is unavailable for overall simplicity.
247                     unsafe = null;
248                 }
249             }
250 
251             if (unsafe != null) {
252                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
253                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
254                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
255                 if (byteArrayIndexScale != 1) {
256                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
257                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
258                     unsafe = null;
259                 }
260             }
261         }
262         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
263         UNSAFE = unsafe;
264 
265         if (unsafe == null) {
266             ADDRESS_FIELD_OFFSET = -1;
267             BYTE_ARRAY_BASE_OFFSET = -1;
268             LONG_ARRAY_BASE_OFFSET = -1;
269             LONG_ARRAY_INDEX_SCALE = -1;
270             INT_ARRAY_BASE_OFFSET = -1;
271             INT_ARRAY_INDEX_SCALE = -1;
272             UNALIGNED = false;
273             DIRECT_BUFFER_CONSTRUCTOR = null;
274             ALLOCATE_ARRAY_METHOD = null;
275             STORE_FENCE_AVAILABLE = false;
276         } else {
277             Constructor<?> directBufferConstructor;
278             long address = -1;
279             try {
280                 final Object maybeDirectBufferConstructor =
281                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
282                             @Override
283                             public Object run() {
284                                 try {
285                                     final Constructor<?> constructor =
286                                             direct.getClass().getDeclaredConstructor(long.class, int.class);
287                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
288                                     if (cause != null) {
289                                         return cause;
290                                     }
291                                     return constructor;
292                                 } catch (NoSuchMethodException e) {
293                                     return e;
294                                 } catch (SecurityException e) {
295                                     return e;
296                                 }
297                             }
298                         });
299 
300                 if (maybeDirectBufferConstructor instanceof Constructor<?>) {
301                     address = UNSAFE.allocateMemory(1);
302                     // try to use the constructor now
303                     try {
304                         ((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
305                         directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
306                         logger.debug("direct buffer constructor: available");
307                     } catch (InstantiationException e) {
308                         directBufferConstructor = null;
309                     } catch (IllegalAccessException e) {
310                         directBufferConstructor = null;
311                     } catch (InvocationTargetException e) {
312                         directBufferConstructor = null;
313                     }
314                 } else {
315                     if (logger.isTraceEnabled()) {
316                         logger.debug("direct buffer constructor: unavailable",
317                                 (Throwable) maybeDirectBufferConstructor);
318                     } else {
319                         logger.debug("direct buffer constructor: unavailable: {}",
320                                 ((Throwable) maybeDirectBufferConstructor).getMessage());
321                     }
322                     directBufferConstructor = null;
323                 }
324             } finally {
325                 if (address != -1) {
326                     UNSAFE.freeMemory(address);
327                 }
328             }
329             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
330             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
331             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
332             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
333             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
334             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
335             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
336             final boolean unaligned;
337             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
338                 @Override
339                 public Object run() {
340                     try {
341                         Class<?> bitsClass =
342                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
343                         int version = javaVersion();
344                         if (unsafeStaticFieldOffsetSupported() && version >= 9) {
345                             // Java9/10 use all lowercase and later versions all uppercase.
346                             String fieldName = version >= 11 ? "UNALIGNED" : "unaligned";
347                             // On Java9 and later we try to directly access the field as we can do this without
348                             // adjust the accessible levels.
349                             try {
350                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
351                                 if (unalignedField.getType() == boolean.class) {
352                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
353                                     Object object = UNSAFE.staticFieldBase(unalignedField);
354                                     return UNSAFE.getBoolean(object, offset);
355                                 }
356                                 // There is something unexpected stored in the field,
357                                 // let us fall-back and try to use a reflective method call as last resort.
358                             } catch (NoSuchFieldException ignore) {
359                                 // We did not find the field we expected, move on.
360                             }
361                         }
362                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
363                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
364                         if (cause != null) {
365                             return cause;
366                         }
367                         return unalignedMethod.invoke(null);
368                     } catch (NoSuchMethodException e) {
369                         return e;
370                     } catch (SecurityException e) {
371                         return e;
372                     } catch (IllegalAccessException e) {
373                         return e;
374                     } catch (ClassNotFoundException e) {
375                         return e;
376                     } catch (InvocationTargetException e) {
377                         return e;
378                     }
379                 }
380             });
381 
382             if (maybeUnaligned instanceof Boolean) {
383                 unaligned = (Boolean) maybeUnaligned;
384                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
385             } else {
386                 String arch = SystemPropertyUtil.get("os.arch", "");
387                 //noinspection DynamicRegexReplaceableByCompiledPattern
388                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
389                 Throwable t = (Throwable) maybeUnaligned;
390                 if (logger.isTraceEnabled()) {
391                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
392                 } else {
393                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
394                 }
395             }
396 
397             UNALIGNED = unaligned;
398 
399             if (javaVersion() >= 9) {
400                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
401                     @Override
402                     public Object run() {
403                         try {
404                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
405                             // sun.misc.Unsafe
406                             Class<?> internalUnsafeClass = getClassLoader(PlatformDependent0.class)
407                                     .loadClass("jdk.internal.misc.Unsafe");
408                             Method method = internalUnsafeClass.getDeclaredMethod("getUnsafe");
409                             return method.invoke(null);
410                         } catch (Throwable e) {
411                             return e;
412                         }
413                     }
414                 });
415                 if (!(maybeException instanceof Throwable)) {
416                     internalUnsafe = maybeException;
417                     final Object finalInternalUnsafe = internalUnsafe;
418                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
419                         @Override
420                         public Object run() {
421                             try {
422                                 return finalInternalUnsafe.getClass().getDeclaredMethod(
423                                         "allocateUninitializedArray", Class.class, int.class);
424                             } catch (NoSuchMethodException e) {
425                                 return e;
426                             } catch (SecurityException e) {
427                                 return e;
428                             }
429                         }
430                     });
431 
432                     if (maybeException instanceof Method) {
433                         try {
434                             Method m = (Method) maybeException;
435                             byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
436                             assert bytes.length == 8;
437                             allocateArrayMethod = m;
438                         } catch (IllegalAccessException e) {
439                             maybeException = e;
440                         } catch (InvocationTargetException e) {
441                             maybeException = e;
442                         }
443                     }
444                 }
445 
446                 if (maybeException instanceof Throwable) {
447                     if (logger.isTraceEnabled()) {
448                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
449                                 (Throwable) maybeException);
450                     } else {
451                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
452                                 ((Throwable) maybeException).getMessage());
453                     }
454                 } else {
455                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
456                 }
457             } else {
458                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
459             }
460             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
461             STORE_FENCE_AVAILABLE = storeFenceAvailable;
462         }
463 
464         if (javaVersion() > 9) {
465             ALIGN_SLICE = (Method) AccessController.doPrivileged(new PrivilegedAction<Object>() {
466                 @Override
467                 public Object run() {
468                     try {
469                         return ByteBuffer.class.getDeclaredMethod("alignedSlice", int.class);
470                     } catch (Exception e) {
471                         return null;
472                     }
473                 }
474             });
475         } else {
476             ALIGN_SLICE = null;
477         }
478 
479         INTERNAL_UNSAFE = internalUnsafe;
480 
481         logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
482                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
483     }
484 
485     private static boolean unsafeStaticFieldOffsetSupported() {
486         return !RUNNING_IN_NATIVE_IMAGE;
487     }
488 
489     static boolean isExplicitNoUnsafe() {
490         return EXPLICIT_NO_UNSAFE_CAUSE != null;
491     }
492 
493     private static Throwable explicitNoUnsafeCause0() {
494         final boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
495         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
496 
497         if (noUnsafe) {
498             logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
499             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
500         }
501 
502         // Legacy properties
503         String unsafePropName;
504         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
505             unsafePropName = "io.netty.tryUnsafe";
506         } else {
507             unsafePropName = "org.jboss.netty.tryUnsafe";
508         }
509 
510         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
511             String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ")";
512             logger.debug(msg);
513             return new UnsupportedOperationException(msg);
514         }
515 
516         return null;
517     }
518 
519     static boolean isUnaligned() {
520         return UNALIGNED;
521     }
522 
523     static boolean hasUnsafe() {
524         return UNSAFE != null;
525     }
526 
527     static Throwable getUnsafeUnavailabilityCause() {
528         return UNSAFE_UNAVAILABILITY_CAUSE;
529     }
530 
531     static boolean unalignedAccess() {
532         return UNALIGNED;
533     }
534 
535     static void throwException(Throwable cause) {
536         // JVM has been observed to crash when passing a null argument. See https://github.com/netty/netty/issues/4131.
537         UNSAFE.throwException(checkNotNull(cause, "cause"));
538     }
539 
540     static boolean hasDirectBufferNoCleanerConstructor() {
541         return DIRECT_BUFFER_CONSTRUCTOR != null;
542     }
543 
544     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
545         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
546     }
547 
548     static ByteBuffer allocateDirectNoCleaner(int capacity) {
549         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
550         // Just use 1 to make it safe to use in all cases:
551         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
552         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity);
553     }
554 
555     static boolean hasAlignSliceMethod() {
556         return ALIGN_SLICE != null;
557     }
558 
559     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
560         try {
561             return (ByteBuffer) ALIGN_SLICE.invoke(buffer, alignment);
562         } catch (IllegalAccessException e) {
563             throw new Error(e);
564         } catch (InvocationTargetException e) {
565             throw new Error(e);
566         }
567     }
568 
569     static boolean hasAllocateArrayMethod() {
570         return ALLOCATE_ARRAY_METHOD != null;
571     }
572 
573     static byte[] allocateUninitializedArray(int size) {
574         try {
575             return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size);
576         } catch (IllegalAccessException e) {
577             throw new Error(e);
578         } catch (InvocationTargetException e) {
579             throw new Error(e);
580         }
581     }
582 
583     static ByteBuffer newDirectBuffer(long address, int capacity) {
584         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
585 
586         try {
587             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
588         } catch (Throwable cause) {
589             // Not expected to ever throw!
590             if (cause instanceof Error) {
591                 throw (Error) cause;
592             }
593             throw new Error(cause);
594         }
595     }
596 
597     static long directBufferAddress(ByteBuffer buffer) {
598         return getLong(buffer, ADDRESS_FIELD_OFFSET);
599     }
600 
601     static long byteArrayBaseOffset() {
602         return BYTE_ARRAY_BASE_OFFSET;
603     }
604 
605     static Object getObject(Object object, long fieldOffset) {
606         return UNSAFE.getObject(object, fieldOffset);
607     }
608 
609     static int getInt(Object object, long fieldOffset) {
610         return UNSAFE.getInt(object, fieldOffset);
611     }
612 
613     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
614         if (STORE_FENCE_AVAILABLE) {
615             UNSAFE.putInt(object, fieldOffset, value);
616             UNSAFE.storeFence();
617         } else {
618             UNSAFE.putIntVolatile(object, fieldOffset, value);
619         }
620     }
621 
622     private static long getLong(Object object, long fieldOffset) {
623         return UNSAFE.getLong(object, fieldOffset);
624     }
625 
626     static long objectFieldOffset(Field field) {
627         return UNSAFE.objectFieldOffset(field);
628     }
629 
630     static byte getByte(long address) {
631         return UNSAFE.getByte(address);
632     }
633 
634     static short getShort(long address) {
635         return UNSAFE.getShort(address);
636     }
637 
638     static int getInt(long address) {
639         return UNSAFE.getInt(address);
640     }
641 
642     static long getLong(long address) {
643         return UNSAFE.getLong(address);
644     }
645 
646     static byte getByte(byte[] data, int index) {
647         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
648     }
649 
650     static byte getByte(byte[] data, long index) {
651         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
652     }
653 
654     static short getShort(byte[] data, int index) {
655         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
656     }
657 
658     static int getInt(byte[] data, int index) {
659         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
660     }
661 
662     static int getInt(int[] data, long index) {
663         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
664     }
665 
666     static int getIntVolatile(long address) {
667         return UNSAFE.getIntVolatile(null, address);
668     }
669 
670     static void putIntOrdered(long adddress, int newValue) {
671         UNSAFE.putOrderedInt(null, adddress, newValue);
672     }
673 
674     static long getLong(byte[] data, int index) {
675         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
676     }
677 
678     static long getLong(long[] data, long index) {
679         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
680     }
681 
682     static void putByte(long address, byte value) {
683         UNSAFE.putByte(address, value);
684     }
685 
686     static void putShort(long address, short value) {
687         UNSAFE.putShort(address, value);
688     }
689 
690     static void putInt(long address, int value) {
691         UNSAFE.putInt(address, value);
692     }
693 
694     static void putLong(long address, long value) {
695         UNSAFE.putLong(address, value);
696     }
697 
698     static void putByte(byte[] data, int index, byte value) {
699         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
700     }
701 
702     static void putByte(Object data, long offset, byte value) {
703         UNSAFE.putByte(data, offset, value);
704     }
705 
706     static void putShort(byte[] data, int index, short value) {
707         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
708     }
709 
710     static void putInt(byte[] data, int index, int value) {
711         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
712     }
713 
714     static void putLong(byte[] data, int index, long value) {
715         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
716     }
717 
718     static void putObject(Object o, long offset, Object x) {
719         UNSAFE.putObject(o, offset, x);
720     }
721 
722     static void copyMemory(long srcAddr, long dstAddr, long length) {
723         // Manual safe-point polling is only needed prior Java9:
724         // See https://bugs.openjdk.java.net/browse/JDK-8149596
725         if (javaVersion() <= 8) {
726             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
727         } else {
728             UNSAFE.copyMemory(srcAddr, dstAddr, length);
729         }
730     }
731 
732     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
733         while (length > 0) {
734             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
735             UNSAFE.copyMemory(srcAddr, dstAddr, size);
736             length -= size;
737             srcAddr += size;
738             dstAddr += size;
739         }
740     }
741 
742     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
743         // Manual safe-point polling is only needed prior Java9:
744         // See https://bugs.openjdk.java.net/browse/JDK-8149596
745         if (javaVersion() <= 8) {
746             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
747         } else {
748             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
749         }
750     }
751 
752     private static void copyMemoryWithSafePointPolling(
753             Object src, long srcOffset, Object dst, long dstOffset, long length) {
754         while (length > 0) {
755             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
756             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
757             length -= size;
758             srcOffset += size;
759             dstOffset += size;
760         }
761     }
762 
763     static void setMemory(long address, long bytes, byte value) {
764         UNSAFE.setMemory(address, bytes, value);
765     }
766 
767     static void setMemory(Object o, long offset, long bytes, byte value) {
768         UNSAFE.setMemory(o, offset, bytes, value);
769     }
770 
771     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
772         int remainingBytes = length & 7;
773         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
774         final long diff = startPos2 - startPos1;
775         if (length >= 8) {
776             final long end = baseOffset1 + remainingBytes;
777             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
778                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
779                     return false;
780                 }
781             }
782         }
783         if (remainingBytes >= 4) {
784             remainingBytes -= 4;
785             long pos = baseOffset1 + remainingBytes;
786             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
787                 return false;
788             }
789         }
790         final long baseOffset2 = baseOffset1 + diff;
791         if (remainingBytes >= 2) {
792             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
793                     (remainingBytes == 2 ||
794                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
795         }
796         return remainingBytes == 0 ||
797                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
798     }
799 
800     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
801         long result = 0;
802         long remainingBytes = length & 7;
803         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
804         final long end = baseOffset1 + remainingBytes;
805         final long diff = startPos2 - startPos1;
806         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
807             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
808         }
809         if (remainingBytes >= 4) {
810             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
811             remainingBytes -= 4;
812         }
813         if (remainingBytes >= 2) {
814             long pos = end - remainingBytes;
815             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
816             remainingBytes -= 2;
817         }
818         if (remainingBytes == 1) {
819             long pos = end - 1;
820             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
821         }
822         return ConstantTimeUtils.equalsConstantTime(result, 0);
823     }
824 
825     static boolean isZero(byte[] bytes, int startPos, int length) {
826         if (length <= 0) {
827             return true;
828         }
829         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
830         int remainingBytes = length & 7;
831         final long end = baseOffset + remainingBytes;
832         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
833             if (UNSAFE.getLong(bytes, i) != 0) {
834                 return false;
835             }
836         }
837 
838         if (remainingBytes >= 4) {
839             remainingBytes -= 4;
840             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
841                 return false;
842             }
843         }
844         if (remainingBytes >= 2) {
845             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
846                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
847         }
848         return bytes[startPos] == 0;
849     }
850 
851     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
852         int hash = HASH_CODE_ASCII_SEED;
853         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
854         final int remainingBytes = length & 7;
855         final long end = baseOffset + remainingBytes;
856         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
857             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
858         }
859         if (remainingBytes == 0) {
860             return hash;
861         }
862         int hcConst = HASH_CODE_C1;
863         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
864             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
865             hcConst = HASH_CODE_C2;
866             baseOffset++;
867         }
868         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
869             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
870             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
871             baseOffset += 2;
872         }
873         if (remainingBytes >= 4) { // 4, 5, 6, 7
874             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
875         }
876         return hash;
877     }
878 
879     static int hashCodeAsciiCompute(long value, int hash) {
880         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
881         // code the same regardless of character case (upper case or lower case hash is the same).
882         return hash * HASH_CODE_C1 +
883                 // Low order int
884                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
885                 // High order int
886                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
887     }
888 
889     static int hashCodeAsciiSanitize(int value) {
890         return value & 0x1f1f1f1f;
891     }
892 
893     static int hashCodeAsciiSanitize(short value) {
894         return value & 0x1f1f;
895     }
896 
897     static int hashCodeAsciiSanitize(byte value) {
898         return value & 0x1f;
899     }
900 
901     static ClassLoader getClassLoader(final Class<?> clazz) {
902         if (System.getSecurityManager() == null) {
903             return clazz.getClassLoader();
904         } else {
905             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
906                 @Override
907                 public ClassLoader run() {
908                     return clazz.getClassLoader();
909                 }
910             });
911         }
912     }
913 
914     static ClassLoader getContextClassLoader() {
915         if (System.getSecurityManager() == null) {
916             return Thread.currentThread().getContextClassLoader();
917         } else {
918             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
919                 @Override
920                 public ClassLoader run() {
921                     return Thread.currentThread().getContextClassLoader();
922                 }
923             });
924         }
925     }
926 
927     static ClassLoader getSystemClassLoader() {
928         if (System.getSecurityManager() == null) {
929             return ClassLoader.getSystemClassLoader();
930         } else {
931             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
932                 @Override
933                 public ClassLoader run() {
934                     return ClassLoader.getSystemClassLoader();
935                 }
936             });
937         }
938     }
939 
940     static int addressSize() {
941         return UNSAFE.addressSize();
942     }
943 
944     static long allocateMemory(long size) {
945         return UNSAFE.allocateMemory(size);
946     }
947 
948     static void freeMemory(long address) {
949         UNSAFE.freeMemory(address);
950     }
951 
952     static long reallocateMemory(long address, long newSize) {
953         return UNSAFE.reallocateMemory(address, newSize);
954     }
955 
956     static boolean isAndroid() {
957         return IS_ANDROID;
958     }
959 
960     private static boolean isAndroid0() {
961         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
962         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
963 
964         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
965         // OpenJDK is used, which means `Unsafe` will actually work as expected.
966 
967         // Android sets this property to Dalvik, regardless of whether it actually is.
968         String vmName = SystemPropertyUtil.get("java.vm.name");
969         boolean isAndroid = "Dalvik".equals(vmName);
970         if (isAndroid) {
971             logger.debug("Platform: Android");
972         }
973         return isAndroid;
974     }
975 
976     private static boolean explicitTryReflectionSetAccessible0() {
977         // we disable reflective access
978         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible", javaVersion() < 9);
979     }
980 
981     static boolean isExplicitTryReflectionSetAccessible() {
982         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
983     }
984 
985     static int javaVersion() {
986         return JAVA_VERSION;
987     }
988 
989     private static int javaVersion0() {
990         final int majorVersion;
991 
992         if (isAndroid0()) {
993             majorVersion = 6;
994         } else {
995             majorVersion = majorVersionFromJavaSpecificationVersion();
996         }
997 
998         logger.debug("Java version: {}", majorVersion);
999 
1000         return majorVersion;
1001     }
1002 
1003     // Package-private for testing only
1004     static int majorVersionFromJavaSpecificationVersion() {
1005         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
1006     }
1007 
1008     // Package-private for testing only
1009     static int majorVersion(final String javaSpecVersion) {
1010         final String[] components = javaSpecVersion.split("\\.");
1011         final int[] version = new int[components.length];
1012         for (int i = 0; i < components.length; i++) {
1013             version[i] = Integer.parseInt(components[i]);
1014         }
1015 
1016         if (version[0] == 1) {
1017             assert version[1] >= 6;
1018             return version[1];
1019         } else {
1020             return version[0];
1021         }
1022     }
1023 
1024     private PlatformDependent0() {
1025     }
1026 }