package me.coley.recaf.util.struct;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Dependent and non-dependent platform utilities for VM.
 *
 * @author xxDark
 */
public final class VMUtil {

    /**
     * Deny all constructions.
     */
    private VMUtil() { }

    /**
     * Appends URL to the {@link URLClassLoader}
     *
     * @param cl  the classloader to add {@link URL} for.
     * @param url the {@link URL} to add.
     */
    public static void addURL(ClassLoader cl, URL url) {
        if (cl instanceof URLClassLoader) {
            addURL0(cl, url);
        } else {
            addURL1(cl, url);
        }
    }

    /**
     * @return running VM version.
     */
    public static int getVmVersion() {
        return (int) (Float.parseFloat(System.getProperty("java.class.version", "52" /* what? */)) - 44);
    }

    private static void addURL0(ClassLoader loader, URL url) {
        Method method;
        try {
            method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException("No 'addURL' method in java.net.URLClassLoader", ex);
        }
        method.setAccessible(true);
        try {
            method.invoke(loader, url);
        } catch (IllegalAccessException ex) {
            throw new IllegalStateException("'addURL' became inaccessible", ex);
        } catch (InvocationTargetException ex) {
            throw new RuntimeException("Error adding URL", ex.getCause());
        }
    }

    private static void addURL1(ClassLoader loader, URL url) {
        Class<?> currentClass =  loader.getClass();
        do {
            Field field;
            try {
                field = currentClass.getDeclaredField("ucp");
            } catch (NoSuchFieldException ignored) {
                continue;
            }
            field.setAccessible(true);
            Object ucp;
            try {
                ucp = field.get(loader);
            } catch (IllegalAccessException ex) {
                throw new IllegalStateException("'ucp' became inaccessible", ex);
            }
            String className;
            if (getVmVersion() < 9) {
                className = "sun.misc.URLClassPath";
            } else {
                className = "jdk.internal.misc.URLClassPath";
            }
            Method method;
            try {
                method = Class.forName(className, true, null).getDeclaredMethod("addURL", URL.class);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException("No 'addURL' method in " + className, ex);
            } catch (ClassNotFoundException ex) {
                throw new RuntimeException(className + " was not found", ex);
            }
            method.setAccessible(true);
            try {
                method.invoke(ucp, url);
                break;
            } catch (IllegalAccessException ex) {
                throw new IllegalStateException("'addURL' became inaccessible", ex);
            } catch (InvocationTargetException ex) {
                throw new RuntimeException("Error adding URL", ex.getCause());
            }
        } while ((currentClass=currentClass.getSuperclass()) != Object.class);
        throw new IllegalArgumentException("No 'ucp' field in " + loader);
    }
}

@@ -1613,37 +1613,47 @@
 ing URL%22, ex.get
-Cause
+TargetException
 ());%0A        %7D%0A 
@@ -3277,13 +3277,23 @@
 .get
-Cause
+TargetException
 ());
@@ -3378,32 +3378,32 @@
  Object.class);%0A
-
         throw ne
@@ -3466,11 +3466,795 @@
 );%0A    %7D
+%0A%0A    /**%0A     * Closes %7B@link URLClassLoader%7D%0A     *%0A     * @param loader%0A     *      Loader to close.%0A     */%0A    public static void close(URLClassLoader loader) %7B%0A        Method method;%0A        try %7B%0A            method = URLClassLoader.class.getDeclaredMethod(%22close%22);%0A        %7D catch (NoSuchMethodException ex) %7B%0A            throw new RuntimeException(%22No 'close' method in java.net.URLClassLoader%22, ex);%0A        %7D%0A        method.setAccessible(true);%0A        try %7B%0A            method.invoke(loader);%0A        %7D catch (IllegalAccessException ex) %7B%0A            throw new IllegalStateException(%22'close' became inaccessible%22, ex);%0A        %7D catch (InvocationTargetException ex) %7B%0A            throw new RuntimeException(%22Error closing loader%22, ex.getTargetException());%0A        %7D%0A    %7D
 %0A%7D%0A

Add VMUtil#close to close url class loaders