/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal;

import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import sun.misc.Cleaner;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;


/**
 * Allows to free direct {@link ByteBuffer} by using {@link Cleaner}. This is encapsulated in an extra class to be able
 * to use {@link PlatformDependent0} on Android without problems.
 *
 * For more details see <a href="https://github.com/netty/netty/issues/2604">#2604</a>.
 */
final class Cleaner0 {
    private static final long CLEANER_FIELD_OFFSET;
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(Cleaner0.class);

    static {
        ByteBuffer direct = ByteBuffer.allocateDirect(1);
        Field cleanerField;
        long fieldOffset = -1;
        if (PlatformDependent0.hasUnsafe()) {
            try {
                cleanerField = direct.getClass().getDeclaredField("cleaner");
                cleanerField.setAccessible(true);
                Cleaner cleaner = (Cleaner) cleanerField.get(direct);
                cleaner.clean();
                fieldOffset = PlatformDependent0.objectFieldOffset(cleanerField);
            } catch (Throwable t) {
                // We don't have ByteBuffer.cleaner().
                fieldOffset = -1;
            }
        }
        logger.debug("java.nio.ByteBuffer.cleaner(): {}", fieldOffset != -1? "available" : "unavailable");
        CLEANER_FIELD_OFFSET = fieldOffset;

        // free buffer if possible
        freeDirectBuffer(direct);
    }

    static void freeDirectBuffer(ByteBuffer buffer) {
        if (CLEANER_FIELD_OFFSET == -1 || !buffer.isDirect()) {
            return;
        }
        try {
            Cleaner cleaner = (Cleaner) PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
            if (cleaner != null) {
                cleaner.clean();
            }
        } catch (Throwable t) {
            // Nothing we can do here.
        }
    }

    private Cleaner0() { }
}

@@ -765,33 +765,8 @@
 ory;
-%0Aimport sun.misc.Cleaner;
 %0A%0Aim
@@ -795,16 +795,49 @@
 .Field;%0A
+import java.lang.reflect.Method;%0A
 import j
@@ -919,23 +919,15 @@
 ing 
-%7B@link 
 Cleaner
-%7D
 . Th
@@ -1210,16 +1210,62 @@
 OFFSET;%0A
+    private static final Method CLEAN_METHOD;%0A
     priv
@@ -1479,32 +1479,61 @@
 eldOffset = -1;%0A
+        Method clean = null;%0A
         if (Plat
@@ -1716,39 +1716,38 @@
                 
-Cleaner
+Object
  cleaner = (Clea
@@ -1740,26 +1740,16 @@
 leaner =
- (Cleaner)
  cleaner
@@ -1771,41 +1771,8 @@
 t);%0A
-                cleaner.clean();%0A
     
@@ -1849,16 +1849,126 @@
 Field);%0A
+                clean = cleaner.getClass().getDeclaredMethod(%22clean%22);%0A                clean.invoke(cleaner);%0A
         
@@ -2076,32 +2076,62 @@
 eldOffset = -1;%0A
+                clean = null;%0A
             %7D%0A  
@@ -2288,16 +2288,46 @@
 dOffset;
+%0A        CLEAN_METHOD = clean;
 %0A%0A      
@@ -2536,32 +2536,129 @@
 turn;%0A        %7D%0A
+        assert CLEAN_METHOD != null : %22CLEANER_FIELD_OFFSET != -1 implies CLEAN_METHOD != null%22;%0A
         try %7B%0A  
@@ -2667,23 +2667,22 @@
         
-Cleaner
+Object
  cleaner
@@ -2687,18 +2687,8 @@
 er =
- (Cleaner)
  Pla
@@ -2799,22 +2799,35 @@
     
+CLEAN_METHOD.invoke(
 cleaner
-.clean(
 );%0A 

Use reflection to call cleaner on direct byte buffers in JDK9.