public static Class<?> defineClass(
      String className,
      Class<?> neighbor,
      ClassLoader loader,
      ProtectionDomain domain,
      byte[] bytecodes) {
    Preconditions.checkNotNull(loader);
    Preconditions.checkArgument(Platform.JAVA_VERSION >= 8);
    if (neighbor != null && Platform.JAVA_VERSION >= 9) {
      // classes in bytecode must be in same package as lookup class.
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      _JDKAccess.addReads(_JDKAccess.getModule(DefineClass.class), _JDKAccess.getModule(neighbor));
      lookup = _Lookup.privateLookupIn(neighbor, lookup);
      return _Lookup.defineClass(lookup, bytecodes);
    }
    if (classloaderDefineClassHandle == null) {
      MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(ClassLoader.class);
      try {
        classloaderDefineClassHandle =
            lookup.findVirtual(
                ClassLoader.class,
                "defineClass",
                MethodType.methodType(
                    Class.class,
                    String.class,
                    byte[].class,
                    int.class,
                    int.class,
                    ProtectionDomain.class));
      } catch (NoSuchMethodException | IllegalAccessException e) {
        throw new RuntimeException(e);
      }
    }
    try {
      return (Class<?>)
          classloaderDefineClassHandle.invokeWithArguments(
              loader, className, bytecodes, 0, bytecodes.length, domain);
    } catch (Throwable e) {
      throw new RuntimeException(e);
    }
  }
@Test
  public void testDefineClass() throws ClassNotFoundException {
    String pkg = DefineClassTest.class.getPackage().getName();
    CompileUnit unit =
        new CompileUnit(
            pkg,
            "A",
            ("package "
                + pkg
                + ";\n"
                + "public class A {\n"
                + "  public static String hello() { return \"HELLO\"; }\n"
                + "}"));
    byte[] bytecodes =
        JaninoUtils.toBytecode(Thread.currentThread().getContextClassLoader(), unit)
            .values()
            .iterator()
            .next();
    String className = pkg + ".A";
    ClassLoaderUtils.ByteArrayClassLoader loader =
        new ClassLoaderUtils.ByteArrayClassLoader(Collections.singletonMap(className, bytecodes));
    loader.loadClass(className);

    loader =
        new ClassLoaderUtils.ByteArrayClassLoader(Collections.singletonMap(className, bytecodes));
    DefineClass.defineClass(className, DefineClassTest.class, loader, null, bytecodes);
    Class<?> clz = loader.loadClass(className);
    if (Platform.JAVA_VERSION >= 9) {
      Assert.assertEquals(clz.getClassLoader(), DefineClassTest.class.getClassLoader());
      Assert.assertThrows(
          Exception.class,
          () ->
              DefineClass.defineClass(
                  className, null, DefineClassTest.class.getClassLoader(), null, bytecodes));
    } else {
      Assert.assertEquals(clz.getClassLoader(), loader);
      DefineClass.defineClass(
          className, null, DefineClassTest.class.getClassLoader(), null, bytecodes);
    }
  }