changeset 7576:d5b536e37988

Sync lambda jdk repo to TL lambda metafactory
author rfield
date Thu, 07 Mar 2013 21:48:41 -0800
parents 4ae1942a996d
children 5344e45a16c5
files src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/share/classes/java/lang/invoke/LambdaMetafactory.java src/share/classes/java/lang/invoke/SerializedLambda.java src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java
diffstat 6 files changed, 206 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Thu Mar 07 16:01:39 2013 -0800
+++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Thu Mar 07 21:48:41 2013 -0800
@@ -66,7 +66,7 @@
     final MethodType implMethodType;          // Type of the implementation method "(int)String"
     final MethodType instantiatedMethodType;  // Instantiated erased functional interface method type "(Integer)Object"
     final boolean isSerializable;             // Should the returned instance be serializable
-    final Class[] markerInterfaces;           // Additional marker interfaces to be implemented
+    final Class<?>[] markerInterfaces;        // Additional marker interfaces to be implemented
 
 
     /**
@@ -94,7 +94,7 @@
                                        MethodHandle implMethod,
                                        MethodType instantiatedMethodType,
                                        int flags,
-                                       Class[] markerInterfaces)
+                                       Class<?>[] markerInterfaces)
             throws ReflectiveOperationException, LambdaConversionException {
         this.targetClass = caller.lookupClass();
         this.invokedType = invokedType;
@@ -111,7 +111,7 @@
         // @@@ Temporary work-around pending resolution of 8005119
         this.implKind = (implInfo.getReferenceKind() == MethodHandleInfo.REF_invokeSpecial)
                         ? MethodHandleInfo.REF_invokeVirtual
-                        : implInfo.getReferenceKind(); 
+                        : implInfo.getReferenceKind();
         this.implIsInstanceMethod =
                 implKind == MethodHandleInfo.REF_invokeVirtual ||
                 implKind == MethodHandleInfo.REF_invokeSpecial ||
@@ -123,15 +123,15 @@
 
         if (!samClass.isInterface()) {
             throw new LambdaConversionException(String.format(
-                    "Functional interface %s is not an interface", 
+                    "Functional interface %s is not an interface",
                     samClass.getName()));
         }
 
         boolean foundSerializableSupertype = Serializable.class.isAssignableFrom(samBase);
-        for (Class c : markerInterfaces) {
+        for (Class<?> c : markerInterfaces) {
             if (!c.isInterface()) {
                 throw new LambdaConversionException(String.format(
-                        "Marker interface %s is not an interface", 
+                        "Marker interface %s is not an interface",
                         c.getName()));
             }
             foundSerializableSupertype |= Serializable.class.isAssignableFrom(c);
@@ -370,21 +370,21 @@
                         // Method matches name and parameter length -- and is not Object
                         if (Modifier.isAbstract(m.getModifiers())) {
                             // Method is abstract
-                                if (m.getReturnType().equals(samReturnType)
+                            if (m.getReturnType().equals(samReturnType)
                                     && Arrays.equals(mParamTypes, samParamTypes)) {
-                                    // Exact match, this is the SAM method signature
-                                    samMethod = m;
-                            } else if (! hasMatchingBridgeSignature(m)) {
+                                // Exact match, this is the SAM method signature
+                                samMethod = m;
+                            } else if (!hasMatchingBridgeSignature(m)) {
                                 // Record bridges, exclude methods with duplicate signatures
-                                    methodsToBridge.add(m);
-                                }
+                                methodsToBridge.add(m);
+                            }
                         } else {
                             // Record default methods for conflict testing
                             defaultMethods.add(m);
-                            }
                         }
                     }
                 }
+            }
             for (Method dm : defaultMethods) {
                 if (hasMatchingBridgeSignature(dm)) {
                     conflictFoundBetweenDefaultAndBridge = true;
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Mar 07 16:01:39 2013 -0800
+++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Mar 07 21:48:41 2013 -0800
@@ -53,7 +53,7 @@
     private static final String NAME_OBJECT = "java/lang/Object";
     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
             = MethodType.methodType(void.class,
-                                    String.class,
+                                    Class.class,
                                     int.class, String.class, String.class, String.class,
                                     int.class, String.class, String.class, String.class,
                                     String.class,
@@ -104,7 +104,7 @@
                                        MethodHandle implMethod,
                                        MethodType instantiatedMethodType,
                                        int flags,
-                                       Class[] markerInterfaces)
+                                       Class<?>[] markerInterfaces)
             throws ReflectiveOperationException, LambdaConversionException {
         super(caller, invokedType, samMethod, implMethod, instantiatedMethodType, flags, markerInterfaces);
         implMethodClassName = implDefiningClass.getName().replace('.', '/');
@@ -177,7 +177,7 @@
      * @return a Class which implements the functional interface
      * @throws LambdaConversionException If properly formed functional interface is not found
      */
-    private <T> Class<? extends T> spinInnerClass() throws LambdaConversionException {
+    private Class<?> spinInnerClass() throws LambdaConversionException {
         String samName = samBase.getName().replace('.', '/');
         String[] interfaces = new String[markerInterfaces.length + 1];
         interfaces[0] = samName;
@@ -210,7 +210,7 @@
         // @@@ The commented-out code is temporary, pending the VM's ability to bridge all methods on request
         // @@@ Once the VM can do fail-over, uncomment the !ma.wasDefaultMethodFound() test, and emit the appropriate
         // @@@ classfile attribute to request custom bridging.  See 8002092.
-        if (!ma.getMethodsToBridge().isEmpty() /* && !ma.wasDefaultMethodFound() */) {
+        if (!ma.getMethodsToBridge().isEmpty() /* && !ma.conflictFoundBetweenDefaultAndBridge() */ ) {
             for (Method m : ma.getMethodsToBridge()) {
                 generateForwardingMethod(m, true);
             }
@@ -247,7 +247,7 @@
             }
         );
 
-        return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
+        return (Class<?>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length,
                                                                    loader, pd);
     }
 
@@ -283,8 +283,8 @@
 
         mv.visitCode();
         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
-        mv.dup();
-        mv.visitLdcInsn(targetClass.getName().replace('.', '/'));
+        mv.visitInsn(DUP);;
+        mv.visitLdcInsn(Type.getType(targetClass));
         mv.visitLdcInsn(samInfo.getReferenceKind());
         mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
         mv.visitLdcInsn(samInfo.getName());
@@ -301,7 +301,7 @@
             mv.visitInsn(DUP);
             mv.iconst(i);
             mv.visitVarInsn(ALOAD, 0);
-            mv.getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+            mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
             mv.boxIfTypePrimitive(argTypes[i]);
             mv.visitInsn(AASTORE);
         }
@@ -345,11 +345,11 @@
 
             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
                 visitTypeInsn(NEW, implMethodClassName);
-                dup();
+                visitInsn(DUP);;
             }
             for (int i = 0; i < argTypes.length; i++) {
                 visitVarInsn(ALOAD, 0);
-                getfield(lambdaClassName, argNames[i], argTypes[i].getDescriptor());
+                visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argTypes[i].getDescriptor());
             }
 
             convertArgumentTypes(Type.getArgumentTypes(m));
@@ -361,7 +361,7 @@
             // Note: if adapting from non-void to void, the 'return' instruction will pop the unneeded result
             Type samReturnType = Type.getReturnType(m);
             convertType(implMethodReturnType, samReturnType, samReturnType);
-            areturn(samReturnType);
+            visitInsn(samReturnType.getOpcode(Opcodes.IRETURN));
 
             visitMaxs(-1, -1); // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
             visitEnd();
@@ -376,7 +376,7 @@
                 Type rcvrType = samArgumentTypes[0];
                 Type instantiatedRcvrType = instantiatedArgumentTypes[0];
 
-                load(lvIndex + 1, rcvrType);
+                visitVarInsn(rcvrType.getOpcode(ILOAD), lvIndex + 1);
                 lvIndex += rcvrType.getSize();
                 convertType(rcvrType, Type.getType(implDefiningClass), instantiatedRcvrType);
             }
@@ -386,7 +386,7 @@
                 Type targetType = implMethodArgumentTypes[argOffset + i];
                 Type instantiatedArgType = instantiatedArgumentTypes[i];
 
-                load(lvIndex + 1, argType);
+                visitVarInsn(argType.getOpcode(ILOAD), lvIndex + 1);
                 lvIndex += argType.getSize();
                 convertType(argType, targetType, instantiatedArgType);
             }
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Thu Mar 07 16:01:39 2013 -0800
+++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Thu Mar 07 21:48:41 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -46,9 +46,9 @@
  * from that in the functional interface. For example, consider
  * <code>interface I&lt;T&gt; { int m(T x); }</code> if this functional interface type is used in a lambda
  * <code>I&lt;Byte&gt; v = ...</code>, we need both the actual functional interface method which has the signature
- * <code>(Object)int</code> and the erased instantiated type of the functional interface method (or simply 
+ * <code>(Object)int</code> and the erased instantiated type of the functional interface method (or simply
  * <I>instantiated method type</I>), which has signature
- * <code>(Byte)int</code>.  
+ * <code>(Byte)int</code>.
  *
  * <p>While functional interfaces only have a single abstract method from the language perspective (concrete
  * methods in Object are and default methods may be present), at the bytecode level they may actually have multiple
@@ -137,7 +137,7 @@
  *     </tr>
  * </table>
  *
- * The default bootstrap ({@link #metaFactory}) represents the common cases and uses an optimized protocol.  
+ * The default bootstrap ({@link #metaFactory}) represents the common cases and uses an optimized protocol.
  * Alternate bootstraps (e.g., {@link #altMetaFactory}) exist to support uncommon cases such as serialization
  * or additional marker superinterfaces.
  *
@@ -153,7 +153,7 @@
      */
     public static final int FLAG_MARKERS = 1 << 1;
 
-    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
 
 /**
      * Standard meta-factory for conversion of lambda expressions or method references to functional interfaces.
@@ -224,20 +224,7 @@
      *                    expected static type of the returned lambda object, and the static types of the captured
      *                    arguments for the lambda.  In the event that the implementation method is an instance method,
      *                    the first argument in the invocation signature will correspond to the receiver.
-     * @param samMethod The primary method in the functional interface to which the lambda or method reference is
-     *                  being converted, represented as a method handle.
-     * @param implMethod The implementation method which should be called (with suitable adaptation of argument
-     *                   types, return types, and adjustment for captured arguments) when methods of the resulting
-     *                   functional interface instance are invoked.
-     * @param instantiatedMethodType The signature of the primary functional interface method after type variables
-     *                               are substituted with their instantiation from the capture site
-     * @param flags A bitmask containing flags that may influence the translation of this lambda expression.  Defined
-     *              fields include FLAG_SERIALIZABLE and FLAG_MARKERS.
-     * @param markerInterfaceCount If the FLAG_MARKERS flag is set, this is a count of the number of additional
-     *                             marker interfaces
-     * @param markerInterfaces If the FLAG_MARKERS flag is set, this consists of Class objects identifying additional
-     *                         marker interfaces which the lambda object should implement, whose count equals
-     *                         markerInterfaceCount
+     * @param  args       argument to pass, flags, marker interface count, and marker interfaces as described above
      * @return a CallSite, which, when invoked, will return an instance of the functional interface
      * @throws ReflectiveOperationException
      * @throws LambdaConversionException If any of the meta-factory protocol invariants are violated
@@ -251,11 +238,11 @@
         MethodHandle implMethod = (MethodHandle)args[1];
         MethodType instantiatedMethodType = (MethodType)args[2];
         int flags = (Integer) args[3];
-        Class[] markerInterfaces;
+        Class<?>[] markerInterfaces;
         int argIndex = 4;
         if ((flags & FLAG_MARKERS) != 0) {
             int markerCount = (Integer) args[argIndex++];
-            markerInterfaces = new Class[markerCount];
+            markerInterfaces = new Class<?>[markerCount];
             System.arraycopy(args, argIndex, markerInterfaces, 0, markerCount);
             argIndex += markerCount;
         }
--- a/src/share/classes/java/lang/invoke/SerializedLambda.java	Thu Mar 07 16:01:39 2013 -0800
+++ b/src/share/classes/java/lang/invoke/SerializedLambda.java	Thu Mar 07 21:48:41 2013 -0800
@@ -39,7 +39,8 @@
  * @see LambdaMetafactory
  */
 public final class SerializedLambda implements Serializable {
-    private final String capturingClass;
+    private static final long serialVersionUID = 8025925345765570181L;
+    private final Class<?> capturingClass;
     private final String functionalInterfaceClass;
     private final String functionalInterfaceMethodName;
     private final String functionalInterfaceMethodSignature;
@@ -52,41 +53,6 @@
     private final Object[] capturedArgs;
 
     /**
-     * Create a {@code SerializedLambda} from the {@code MethodHandle} and {@code MethodType} information
-     * present at the lambda factory site.
-     *
-     * @param capturingClass The class in which the lambda expression appears
-     * @param functionalInterface A {@code MethodHandle} representing the functional interface descriptor
-     *                            for the lambda's target type
-     * @param implementation A {@code MethodHandle} identifying the method that implements the lambda expression's
-     *                       body, with any captured arguments prepended to the beginning of the argument list
-     * @param instantiatedMethodType The signature of the primary functional interface method after type variables
-     *                               are substituted with their instantiation from the capture site
-     * @param capturedArgs The dynamic arguments to the lambda factory site, which represent variables captured by
-     *                     the lambda
-     * @throws ReflectiveOperationException
-     */
-    public SerializedLambda(Class capturingClass,
-                            MethodHandle functionalInterface,
-                            MethodHandle implementation,
-                            MethodType instantiatedMethodType,
-                            Object[] capturedArgs) throws ReflectiveOperationException {
-        MethodHandleInfo samMhi = new MethodHandleInfo(Objects.requireNonNull(functionalInterface));
-        MethodHandleInfo implMhi = new MethodHandleInfo(Objects.requireNonNull(implementation));
-        this.capturingClass = Objects.requireNonNull(capturingClass).getName();
-        this.capturedArgs = Objects.requireNonNull(capturedArgs).clone();
-        this.functionalInterfaceClass = samMhi.getDeclaringClass().getName();
-        this.functionalInterfaceMethodName = samMhi.getName();
-        this.functionalInterfaceMethodSignature = samMhi.getMethodType().toMethodDescriptorString();
-        this.functionalInterfaceMethodKind = samMhi.getReferenceKind();
-        this.implClass = implMhi.getClass().getName();
-        this.implMethodName = implMhi.getName();
-        this.implMethodSignature = implMhi.getMethodType().toMethodDescriptorString();
-        this.implMethodKind = implMhi.getReferenceKind();
-        this.instantiatedMethodType = instantiatedMethodType.toMethodDescriptorString();
-    }
-
-    /**
      * Create a {@code SerializedLambda} from the low-level information present at the lambda factory site.
      *
      * @param capturingClass The class in which the lambda expression appears
@@ -107,7 +73,7 @@
      * @param capturedArgs The dynamic arguments to the lambda factory site, which represent variables captured by
      *                     the lambda
      */
-    public SerializedLambda(String capturingClass,
+    public SerializedLambda(Class<?> capturingClass,
                             int functionalInterfaceMethodKind,
                             String functionalInterfaceClass,
                             String functionalInterfaceMethodName,
@@ -133,7 +99,7 @@
 
     /** Get the name of the class that captured this lambda */
     public String getCapturingClass() {
-        return capturingClass;
+        return capturingClass.getName().replace('.', '/');
     }
 
     /** Get the name of the functional interface class to which this lambda has been converted */
@@ -200,9 +166,7 @@
             Method deserialize = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
                 @Override
                 public Method run() throws Exception {
-                    Class<?> clazz = Class.forName(capturingClass.replace('/', '.'), true,
-                                                   Thread.currentThread().getContextClassLoader());
-                    Method m = clazz.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
+                    Method m = capturingClass.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
                     m.setAccessible(true);
                     return m;
                 }
@@ -230,14 +194,4 @@
                              MethodHandleInfo.getReferenceKindString(implMethodKind), implClass, implMethodName,
                              implMethodSignature, instantiatedMethodType, capturedArgs.length);
     }
-
-    /*
-    // @@@ Review question: is it worthwhile implementing a versioned serialization protocol?
-
-    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
-    }
-
-    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
-    }
-*/
 }
--- a/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Thu Mar 07 16:01:39 2013 -0800
+++ b/src/share/classes/java/lang/invoke/TypeConvertingMethodAdapter.java	Thu Mar 07 21:48:41 2013 -0800
@@ -52,7 +52,7 @@
 
     // Table of wrappers for primitives, indexed by ASM type sorts
     private static final Wrapper[] FROM_TYPE_SORT = new Wrapper[16];
-    
+
     static {
         for (Wrapper w : Wrapper.values()) {
             if (w.basicTypeChar() != 'L') {
@@ -280,13 +280,10 @@
     }
 
     /**
-     * The following methods are copied from
+     * The following method is copied from
      * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
-     * and fast Java bytecode manipulation framework. 
+     * and fast Java bytecode manipulation framework.
      * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
-     *
-     * Subclass with that (removing these methods) if that package/class is ever
-     * added to the JDK.
      */
     void iconst(final int cst) {
         if (cst >= -1 && cst <= 5) {
@@ -299,23 +296,4 @@
             mv.visitLdcInsn(cst);
         }
     }
-
-    void load(final int var, final Type type) {
-        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
-    }
-
-    void dup() {
-        mv.visitInsn(Opcodes.DUP);
-    }
-
-    void areturn(final Type t) {
-        mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
-    }
-
-    void getfield(
-            final String owner,
-            final String name,
-            final String desc) {
-        mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java	Thu Mar 07 21:48:41 2013 -0800
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+@test
+@bug 8004970
+@summary Lambda serialization in the presence of class loaders
+@author Peter Levart
+*/
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class LambdaClassLoaderSerialization {
+
+    public interface SerializableRunnable extends Runnable, Serializable {}
+
+    public static class MyCode implements SerializableRunnable {
+
+        private byte[] serialize(Object o) {
+            ByteArrayOutputStream baos;
+            try (
+                ObjectOutputStream oos =
+                    new ObjectOutputStream(baos = new ByteArrayOutputStream())
+            ) {
+                oos.writeObject(o);
+            }
+            catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            return baos.toByteArray();
+        }
+
+        private <T> T deserialize(byte[] bytes) {
+            try (
+                ObjectInputStream ois =
+                    new ObjectInputStream(new ByteArrayInputStream(bytes))
+            ) {
+                return (T) ois.readObject();
+            }
+            catch (IOException | ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void run() {
+            System.out.println("                this: " + this);
+
+            SerializableRunnable deSerializedThis = deserialize(serialize(this));
+            System.out.println("    deSerializedThis: " + deSerializedThis);
+
+            SerializableRunnable runnable = () -> {System.out.println("HELLO");};
+            System.out.println("            runnable: " + runnable);
+
+            SerializableRunnable deSerializedRunnable = deserialize(serialize(runnable));
+            System.out.println("deSerializedRunnable: " + deSerializedRunnable);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ClassLoader myCl = new MyClassLoader(
+            LambdaClassLoaderSerialization.class.getClassLoader()
+        );
+        Class<?> myCodeClass = Class.forName(
+            LambdaClassLoaderSerialization.class.getName() + "$MyCode",
+            true,
+            myCl
+        );
+        Runnable myCode = (Runnable) myCodeClass.newInstance();
+        myCode.run();
+    }
+
+    static class MyClassLoader extends ClassLoader {
+        MyClassLoader(ClassLoader parent) {
+            super(parent);
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (name.indexOf('.') < 0) {
+                synchronized (getClassLoadingLock(name)) {
+                    Class<?> c = findLoadedClass(name);
+                    if (c == null) {
+                        c = findClass(name);
+                    }
+                    if (resolve) {
+                        resolveClass(c);
+                    }
+                    return c;
+                }
+            } else {
+                return super.loadClass(name, resolve);
+            }
+        }
+
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            String path = name.replace('.', '/').concat(".class");
+            try (InputStream is = getResourceAsStream(path)) {
+                if (is != null) {
+                    byte[] bytes = readFully(is);
+                    return defineClass(name, bytes, 0, bytes.length);
+                } else {
+                    throw new ClassNotFoundException(name);
+                }
+            }
+            catch (IOException e) {
+                throw new ClassNotFoundException(name, e);
+            }
+        }
+
+        static byte[] readFully(InputStream is) throws IOException {
+            byte[] output = {};
+            int pos = 0;
+            while (true) {
+                int bytesToRead;
+                if (pos >= output.length) { // Only expand when there's no room
+                    bytesToRead = output.length + 1024;
+                    if (output.length < pos + bytesToRead) {
+                        output = Arrays.copyOf(output, pos + bytesToRead);
+                    }
+                } else {
+                    bytesToRead = output.length - pos;
+                }
+                int cc = is.read(output, pos, bytesToRead);
+                if (cc < 0) {
+                    if (output.length != pos) {
+                        output = Arrays.copyOf(output, pos);
+                    }
+                    break;
+                }
+                pos += cc;
+            }
+            return output;
+        }
+    }
+}