diff --git a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java
index e5a1d044676..0c5bea001ec 100644
--- a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java
+++ b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java
@@ -116,6 +116,12 @@ public static void main(String ... args) throws Throwable {
                 DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.ROOT)
                         .format(new Date()));
 
+        // A selection of trivial and common reflection operations
+        var instance = HelloClasslist.class.getConstructor().newInstance();
+        HelloClasslist.class.getMethod("staticMethod_V").invoke(null);
+        var obj = HelloClasslist.class.getMethod("staticMethod_L_L", Object.class).invoke(null, instance);
+        HelloClasslist.class.getField("field").get(instance);
+
         // A selection of trivial and relatively common MH operations
         invoke(MethodHandles.identity(double.class), 1.0);
         invoke(MethodHandles.identity(int.class), 1);
@@ -126,8 +132,14 @@ public static void main(String ... args) throws Throwable {
         LOGGER.log(Level.FINE, "New Date: " + newDate + " - old: " + oldDate);
     }
 
+    public HelloClasslist() {}
+
+    public String field = "someValue";
+
     public static void staticMethod_V() {}
 
+    public static Object staticMethod_L_L(Object o) { return o; }
+
     private static MethodHandle handle(String name, MethodType type) throws Throwable {
         return MethodHandles.lookup().findStatic(HelloClasslist.class, name, type);
     }
diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
index 708433ab45d..75f999b54b7 100644
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -251,6 +251,7 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
     return false;
   // Even if general trusting is disabled, trust system-built closures in these packages.
   if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
+      holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") ||
       holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
       holder->is_in_package("jdk/internal/vm/vector") || holder->is_in_package("jdk/incubator/vector") ||
       holder->is_in_package("java/lang"))
diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
index 98a51432933..b668be4e13e 100644
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -73,6 +73,7 @@
 import jdk.internal.misc.Unsafe;
 import jdk.internal.module.Resources;
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.ConstantPool;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.reflect.ReflectionFactory;
@@ -387,9 +388,15 @@ static String typeVarBounds(TypeVariable<?> typeVar) {
     public static Class<?> forName(String className)
                 throws ClassNotFoundException {
         Class<?> caller = Reflection.getCallerClass();
-        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
+        return forName(className, caller);
     }
 
+    // Caller-sensitive adapter method for reflective invocation
+    @CallerSensitiveAdapter
+    private static Class<?> forName(String className, Class<?> caller)
+            throws ClassNotFoundException {
+        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
+    }
 
     /**
      * Returns the {@code Class} object associated with the class or
@@ -471,11 +478,25 @@ public static Class<?> forName(String name, boolean initialize,
             // Reflective call to get caller class is only needed if a security manager
             // is present.  Avoid the overhead of making this call otherwise.
             caller = Reflection.getCallerClass();
+        }
+        return forName(name, initialize, loader, caller);
+    }
+
+    // Caller-sensitive adapter method for reflective invocation
+    @CallerSensitiveAdapter
+    private static Class<?> forName(String name, boolean initialize, ClassLoader loader, Class<?> caller)
+            throws ClassNotFoundException
+    {
+        @SuppressWarnings("removal")
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            // Reflective call to get caller class is only needed if a security manager
+            // is present.  Avoid the overhead of making this call otherwise.
             if (loader == null) {
                 ClassLoader ccl = ClassLoader.getClassLoader(caller);
                 if (ccl != null) {
                     sm.checkPermission(
-                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
+                            SecurityConstants.GET_CLASSLOADER_PERMISSION);
                 }
             }
         }
@@ -538,13 +559,24 @@ private static native Class<?> forName0(String name, boolean initialize,
     @SuppressWarnings("removal")
     @CallerSensitive
     public static Class<?> forName(Module module, String name) {
+        Class<?> caller = null;
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            caller = Reflection.getCallerClass();
+        }
+        return forName(module, name, caller);
+    }
+
+    // Caller-sensitive adapter method for reflective invocation
+    @SuppressWarnings("removal")
+    @CallerSensitiveAdapter
+    private static Class<?> forName(Module module, String name, Class<?> caller) {
         Objects.requireNonNull(module);
         Objects.requireNonNull(name);
 
         ClassLoader cl;
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            Class<?> caller = Reflection.getCallerClass();
             if (caller != null && caller.getModule() != module) {
                 // if caller is null, Class.forName is the last java frame on the stack.
                 // java.base has all permissions
diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java
index e1ad30f5b11..dffd56948f3 100644
--- a/src/java.base/share/classes/java/lang/ClassLoader.java
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java
@@ -64,6 +64,7 @@
 import jdk.internal.misc.Unsafe;
 import jdk.internal.misc.VM;
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.util.StaticProperty;
 import sun.reflect.misc.ReflectUtil;
@@ -1615,9 +1616,13 @@ protected Enumeration<URL> findResources(String name) throws IOException {
      */
     @CallerSensitive
     protected static boolean registerAsParallelCapable() {
-        Class<? extends ClassLoader> callerClass =
-            Reflection.getCallerClass().asSubclass(ClassLoader.class);
-        return ParallelLoaders.register(callerClass);
+        return registerAsParallelCapable(Reflection.getCallerClass());
+    }
+
+    // Caller-sensitive adapter method for reflective invocation
+    @CallerSensitiveAdapter
+    private static boolean registerAsParallelCapable(Class<?> caller) {
+        return ParallelLoaders.register(caller.asSubclass(ClassLoader.class));
     }
 
     /**
diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
index 6b50cf2a2aa..30014e1799a 100644
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
@@ -26,7 +26,6 @@
 package java.lang.invoke;
 
 import jdk.internal.misc.CDS;
-import jdk.internal.misc.VM;
 import jdk.internal.org.objectweb.asm.*;
 import sun.invoke.util.BytecodeDescriptor;
 import sun.invoke.util.VerifyAccess;
@@ -37,7 +36,6 @@
 import java.io.Serializable;
 import java.lang.constant.ConstantDescs;
 import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -46,8 +44,10 @@
 import java.util.PropertyPermission;
 import java.util.Set;
 
+import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
+import static java.lang.invoke.MethodType.methodType;
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 
 /**
@@ -57,7 +57,6 @@
  * @see LambdaMetafactory
  */
 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
-    private static final int CLASSFILE_VERSION = VM.classFileVersion();
     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
     private static final String NAME_CTOR = "<init>";
@@ -106,7 +105,7 @@
         disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
 
         // condy to load implMethod from class data
-        MethodType classDataMType = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
+        MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
         Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
                                          classDataMType.descriptorString(), false);
         implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
@@ -227,50 +226,28 @@ private static String lambdaClassName(Class<?> targetClass) {
     @Override
     CallSite buildCallSite() throws LambdaConversionException {
         final Class<?> innerClass = spinInnerClass();
-        if (factoryType.parameterCount() == 0) {
-            // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
-            // unless we've suppressed eager initialization
-            if (disableEagerInitialization) {
-                try {
-                    return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
-                            factoryType.returnType()));
-                } catch (ReflectiveOperationException e) {
-                    throw new LambdaConversionException(
-                            "Exception finding " +  LAMBDA_INSTANCE_FIELD + " static field", e);
-                }
-            } else {
-                @SuppressWarnings("removal")
-                final Constructor<?>[] ctrs = AccessController.doPrivileged(
-                        new PrivilegedAction<>() {
-                            @Override
-                            public Constructor<?>[] run() {
-                                Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
-                                if (ctrs.length == 1) {
-                                    // The lambda implementing inner class constructor is private, set
-                                    // it accessible (by us) before creating the constant sole instance
-                                    ctrs[0].setAccessible(true);
-                                }
-                                return ctrs;
-                            }
-                        });
-                if (ctrs.length != 1) {
-                    throw new LambdaConversionException("Expected one lambda constructor for "
-                            + innerClass.getCanonicalName() + ", got " + ctrs.length);
-                }
-
-                try {
-                    Object inst = ctrs[0].newInstance();
-                    return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
-                } catch (ReflectiveOperationException e) {
-                    throw new LambdaConversionException("Exception instantiating lambda object", e);
-                }
+        if (factoryType.parameterCount() == 0 && disableEagerInitialization) {
+            try {
+                return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
+                                                                    factoryType.returnType()));
+            } catch (ReflectiveOperationException e) {
+                throw new LambdaConversionException(
+                        "Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e);
             }
         } else {
             try {
                 MethodHandle mh = caller.findConstructor(innerClass, constructorType);
-                return new ConstantCallSite(mh.asType(factoryType));
+                if (factoryType.parameterCount() == 0) {
+                    // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance
+                    Object inst = mh.asType(methodType(Object.class)).invokeExact();
+                    return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
+                } else {
+                    return new ConstantCallSite(mh.asType(factoryType));
+                }
             } catch (ReflectiveOperationException e) {
                 throw new LambdaConversionException("Exception finding constructor", e);
+            } catch (Throwable e) {
+                throw new LambdaConversionException("Exception instantiating lambda object", e);
             }
         }
     }
diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
index 50a916696c3..6a7d0924c6f 100644
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
@@ -44,6 +44,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Stream;
 
 import static java.lang.invoke.LambdaForm.BasicType;
@@ -316,7 +317,7 @@ private static String debugString(Object arg) {
      * Extract the MemberName of a newly-defined method.
      */
     private MemberName loadMethod(byte[] classFile) {
-        Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile)
+        Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of())
                                       .defineClass(true, classDataValues());
         return resolveInvokerMember(invokerClass, invokerName, invokerType);
     }
@@ -376,7 +377,7 @@ static void clinit(ClassWriter cw, String className, List<ClassData> classData)
         MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
         mv.visitCode();
         mv.visitLdcInsn(Type.getType("L" + className + ";"));
-        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandles",
                            "classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
         // we should optimize one single element case that does not need to create a List
         mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
index c65710586dc..a428380443e 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -42,6 +42,7 @@
 
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.nio.ByteOrder;
 import java.util.Arrays;
@@ -51,6 +52,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.stream.Stream;
@@ -58,6 +60,7 @@
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
 import static jdk.internal.org.objectweb.asm.Opcodes.*;
 
 /**
@@ -1031,6 +1034,7 @@ static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
     // That way we can lazily load the code and set up the constants.
     private static class BindCaller {
         private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+        private static MethodType REFLECT_INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object.class, Object[].class);
 
         static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
             // Code in the boot layer should now be careful while creating method handles or
@@ -1043,15 +1047,48 @@ static MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
                        hostClass.getName().startsWith("java.lang.invoke."))) {
                 throw new InternalError();  // does not happen, and should not anyway
             }
+
+            MemberName member = mh.internalMemberName();
+            if (member != null) {
+                // Look up the CSM adapter method with the same method name
+                // but with an additional caller class parameter.  If present,
+                // bind the adapter's method handle with the lookup class as
+                // the caller class argument
+                MemberName csmAdapter = IMPL_LOOKUP.resolveOrNull(member.getReferenceKind(),
+                        new MemberName(member.getDeclaringClass(),
+                                       member.getName(),
+                                       member.getMethodType().appendParameterTypes(Class.class),
+                                       member.getReferenceKind()));
+                if (csmAdapter != null) {
+                    assert !csmAdapter.isCallerSensitive();
+                    MethodHandle dmh = DirectMethodHandle.make(csmAdapter);
+                    dmh = MethodHandles.insertArguments(dmh, dmh.type().parameterCount() - 1, hostClass);
+                    dmh = new WrappedMember(dmh, mh.type(), member, mh.isInvokeSpecial(), hostClass);
+                    return dmh;
+                }
+            }
+
+            // If no adapter method for CSM with an additional Class parameter
+            // is present, then inject an invoker class that is the caller
+            // invoking the method handle of the CSM
+            try {
+                return bindCallerWithInjectedInvoker(mh, hostClass);
+            } catch (ReflectiveOperationException ex) {
+                throw uncaughtException(ex);
+            }
+        }
+
+        private static MethodHandle bindCallerWithInjectedInvoker(MethodHandle mh, Class<?> hostClass)
+                throws ReflectiveOperationException
+        {
             // For simplicity, convert mh to a varargs-like method.
             MethodHandle vamh = prepareForInvoker(mh);
             // Cache the result of makeInjectedInvoker once per argument class.
-            MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
+            MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass).invoker();
             return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
         }
 
-        private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
-            try {
+        private static Class<?> makeInjectedInvoker(Class<?> targetClass) {
                 /*
                  * The invoker class defined to the same class loader as the lookup class
                  * but in an unnamed package so that the class bytes can be cached and
@@ -1065,21 +1102,80 @@ private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
                     name = name.replace('/', '_');
                 }
                 Class<?> invokerClass = new Lookup(targetClass)
-                        .makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE)
-                        .defineClass(true);
+                        .makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE))
+                        .defineClass(true, targetClass);
                 assert checkInjectedInvoker(targetClass, invokerClass);
-                return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
-            } catch (ReflectiveOperationException ex) {
-                throw uncaughtException(ex);
-            }
+                return invokerClass;
         }
 
-        private static ClassValue<MethodHandle> CV_makeInjectedInvoker = new ClassValue<MethodHandle>() {
-            @Override protected MethodHandle computeValue(Class<?> hostClass) {
-                return makeInjectedInvoker(hostClass);
+        private static ClassValue<InjectedInvokerHolder> CV_makeInjectedInvoker = new ClassValue<>() {
+            @Override
+            protected InjectedInvokerHolder computeValue(Class<?> hostClass) {
+                return new InjectedInvokerHolder(makeInjectedInvoker(hostClass));
             }
         };
 
+        /*
+         * Returns a method handle of an invoker class injected for reflection
+         * implementation use with the following signature:
+         *     reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
+         *
+         * Method::invoke on a caller-sensitive method will call
+         * MethodAccessorImpl::invoke(Object, Object[]) through reflect_invoke_V
+         *     target.csm(args)
+         *     NativeMethodAccesssorImpl::invoke(target, args)
+         *     MethodAccessImpl::invoke(target, args)
+         *     InjectedInvoker::reflect_invoke_V(vamh, target, args);
+         *     method::invoke(target, args)
+         *     p.Foo::m
+         *
+         * An injected invoker class is a hidden class which has the same
+         * defining class loader, runtime package, and protection domain
+         * as the given caller class.
+         */
+        static MethodHandle reflectiveInvoker(Class<?> caller) {
+            return BindCaller.CV_makeInjectedInvoker.get(caller).reflectInvoker();
+        }
+
+        private static final class InjectedInvokerHolder {
+            private final Class<?> invokerClass;
+            // lazily resolved and cached DMH(s) of invoke_V methods
+            private MethodHandle invoker;
+            private MethodHandle reflectInvoker;
+
+            private InjectedInvokerHolder(Class<?> invokerClass) {
+                this.invokerClass = invokerClass;
+            }
+
+            private MethodHandle invoker() {
+                var mh = invoker;
+                if (mh == null) {
+                    try {
+                        invoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
+                    } catch (Error | RuntimeException ex) {
+                        throw ex;
+                    } catch (Throwable ex) {
+                        throw new InternalError(ex);
+                    }
+                }
+                return mh;
+            }
+
+            private MethodHandle reflectInvoker() {
+                var mh = reflectInvoker;
+                if (mh == null) {
+                    try {
+                        reflectInvoker = mh = IMPL_LOOKUP.findStatic(invokerClass, "reflect_invoke_V", REFLECT_INVOKER_MT);
+                    } catch (Error | RuntimeException ex) {
+                        throw ex;
+                    } catch (Throwable ex) {
+                        throw new InternalError(ex);
+                    }
+                }
+                return mh;
+            }
+        }
+
         // Adapt mh so that it can be called directly from an injected invoker:
         private static MethodHandle prepareForInvoker(MethodHandle mh) {
             mh = mh.asFixedArity();
@@ -1116,6 +1212,8 @@ private static boolean checkInjectedInvoker(Class<?> hostClass, Class<?> invoker
                 MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
                 MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
                 return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass });
+            } catch (Error|RuntimeException ex) {
+                throw ex;
             } catch (Throwable ex) {
                 throw new InternalError(ex);
             }
@@ -1152,27 +1250,50 @@ private static byte[] generateInvokerTemplate() {
             ClassWriter cw = new ClassWriter(0);
 
             // private static class InjectedInvoker {
+            //     /* this is used to wrap DMH(s) of caller-sensitive methods */
             //     @Hidden
             //     static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
             //        return vamh.invokeExact(args);
             //     }
+            //     /* this is used in caller-sensitive reflective method accessor */
+            //     @Hidden
+            //     static Object reflect_invoke_V(MethodHandle vamh, Object target, Object[] args) throws Throwable {
+            //        return vamh.invokeExact(target, args);
+            //     }
+            // }
             // }
             cw.visit(CLASSFILE_VERSION, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null);
+            {
+                var mv = cw.visitMethod(ACC_STATIC, "invoke_V",
+                        "(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
+                        null, null);
+
+                mv.visitCode();
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(ALOAD, 1);
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
+                        "([Ljava/lang/Object;)Ljava/lang/Object;", false);
+                mv.visitInsn(ARETURN);
+                mv.visitMaxs(2, 2);
+                mv.visitEnd();
+
+                cw.visitEnd();
+            }
 
-            MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V",
-                          "(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
-                          null, null);
-
-            mv.visitCode();
-            mv.visitVarInsn(ALOAD, 0);
-            mv.visitVarInsn(ALOAD, 1);
-            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
-                               "([Ljava/lang/Object;)Ljava/lang/Object;", false);
-            mv.visitInsn(ARETURN);
-            mv.visitMaxs(2, 2);
-            mv.visitEnd();
-
-            cw.visitEnd();
+            {
+                var mv = cw.visitMethod(ACC_STATIC, "reflect_invoke_V",
+                        "(Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
+                        null, null);
+                mv.visitCode();
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitVarInsn(ALOAD, 1);
+                mv.visitVarInsn(ALOAD, 2);
+                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
+                        "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
+                mv.visitInsn(ARETURN);
+                mv.visitMaxs(3, 3);
+                mv.visitEnd();
+            }
             return cw.toByteArray();
         }
     }
@@ -1505,15 +1626,47 @@ public VarHandle insertCoordinates(VarHandle target, int pos, Object... values)
                 return VarHandles.insertCoordinates(target, pos, values);
             }
 
+
+            @Override
+            public MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException {
+                return IMPL_LOOKUP.unreflectConstructor(ctor);
+            }
+
+            @Override
+            public MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException {
+                return isSetter ? IMPL_LOOKUP.unreflectSetter(field) : IMPL_LOOKUP.unreflectGetter(field);
+            }
+
             @Override
-            public MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException {
-                return IMPL_LOOKUP.findStatic(cls, name, methodType);
+            public MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
+                try {
+                    return IMPL_LOOKUP.findVirtual(defc, name, type);
+                } catch (NoSuchMethodException e) {
+                    return null;
+                }
             }
 
             @Override
-            public MethodHandle unreflectGetter(Field field) throws IllegalAccessException {
-                return IMPL_LOOKUP.unreflectGetter(field);
+            public MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException {
+                try {
+                    return IMPL_LOOKUP.findStatic(defc, name, type);
+                } catch (NoSuchMethodException e) {
+                    return null;
+                }
             }
+
+            @Override
+            public MethodHandle reflectiveInvoker(Class<?> caller) {
+                Objects.requireNonNull(caller);
+                return BindCaller.reflectiveInvoker(caller);
+            }
+
+            @Override
+            public Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize) {
+                // skip name and access flags validation
+                return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
+            }
+
         });
     }
 
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
index abb9eefb01a..96c1a0f6bcf 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java
@@ -25,8 +25,7 @@
 
 package java.lang.invoke;
 
-import jdk.internal.access.JavaLangAccess;
-import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
 import jdk.internal.ref.CleanerFactory;
 import sun.invoke.util.Wrapper;
 
@@ -35,7 +34,6 @@
 
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
-import static java.lang.invoke.MethodHandleStatics.UNSAFE;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 
 /**
@@ -248,6 +246,7 @@ static boolean verifyConstants() {
         return true;
     }
     static {
+        VM.setJavaLangInvokeInited();
         assert(verifyConstants());
     }
 
@@ -667,8 +666,7 @@ static boolean isCallerSensitive(MemberName mem) {
 
     static boolean canBeCalledVirtual(MemberName mem) {
         assert(mem.isInvocable());
-        return mem.getName().equals("getContextClassLoader") &&
-            canBeCalledVirtual(mem, java.lang.Thread.class);
+        return mem.getName().equals("getContextClassLoader") && canBeCalledVirtual(mem, java.lang.Thread.class);
     }
 
     static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
@@ -678,16 +676,4 @@ static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass
         return (definingClass.isAssignableFrom(symbolicRefClass) ||  // Msym overrides Mdef
                 symbolicRefClass.isInterface());                     // Mdef implements Msym
     }
-
-    private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
-    /*
-     * Returns the class data set by the VM in the Class::classData field.
-     *
-     * This is also invoked by LambdaForms as it cannot use condy via
-     * MethodHandles.classData due to bootstrapping issue.
-     */
-    static Object classData(Class<?> c) {
-        UNSAFE.ensureClassInitialized(c);
-        return JLA.classData(c);
-    }
 }
diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 9e5e590cb84..5b4db852421 100644
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -32,6 +32,7 @@
 import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.org.objectweb.asm.Type;
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.vm.annotation.ForceInline;
 import sun.invoke.util.ValueConversions;
@@ -63,7 +64,9 @@
 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.UNSAFE;
 import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
+import static java.lang.invoke.MethodHandleStatics.newInternalError;
 import static java.lang.invoke.MethodType.methodType;
 
 /**
@@ -117,14 +120,15 @@ public static Lookup lookup() {
     }
 
     /**
-     * This reflected$lookup method is the alternate implementation of
-     * the lookup method when being invoked by reflection.
+     * This lookup method is the alternate implementation of
+     * the lookup method with a leading caller class argument which is
+     * non-caller-sensitive.  This method is only invoked by reflection
+     * and method handle.
      */
-    @CallerSensitive
-    private static Lookup reflected$lookup() {
-        Class<?> caller = Reflection.getCallerClass();
+    @CallerSensitiveAdapter
+    private static Lookup lookup(Class<?> caller) {
         if (caller.getClassLoader() == null) {
-            throw newIllegalArgumentException("illegal lookupClass: "+caller);
+            throw newInternalError("calling lookup() reflectively is not supported: "+caller);
         }
         return new Lookup(caller);
     }
@@ -329,7 +333,7 @@ public static <T> T classData(Lookup caller, String name, Class<T> type) throws
              throw new IllegalAccessException(caller + " does not have ORIGINAL access");
          }
 
-         Object classdata = MethodHandleNatives.classData(caller.lookupClass());
+         Object classdata = classData(caller.lookupClass());
          if (classdata == null) return null;
 
          try {
@@ -341,6 +345,17 @@ public static <T> T classData(Lookup caller, String name, Class<T> type) throws
          }
     }
 
+    /*
+     * Returns the class data set by the VM in the Class::classData field.
+     *
+     * This is also invoked by LambdaForms as it cannot use condy via
+     * MethodHandles::classData due to bootstrapping issue.
+     */
+    static Object classData(Class<?> c) {
+        UNSAFE.ensureClassInitialized(c);
+        return SharedSecrets.getJavaLangAccess().classData(c);
+    }
+
     /**
      * Returns the element at the specified index in the
      * {@linkplain #classData(Lookup, String, Class) class data},
@@ -2360,15 +2375,16 @@ ClassDefiner makeHiddenClassDefiner(byte[] bytes,
 
         /**
          * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
-         * from the given bytes.  No package name check on the given name.
+         * from the given bytes and the given options.  No package name check on the given name.
          *
          * @param name    fully-qualified name that specifies the prefix of the hidden class
          * @param bytes   class bytes
-         * @return ClassDefiner that defines a hidden class of the given bytes.
+         * @param options class options
+         * @return ClassDefiner that defines a hidden class of the given bytes and options.
          */
-        ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
+        ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, Set<ClassOption> options) {
             // skip name and access flags validation
-            return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), Set.of(), false);
+            return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), options, false);
         }
 
         /**
diff --git a/src/java.base/share/classes/java/lang/reflect/Constructor.java b/src/java.base/share/classes/java/lang/reflect/Constructor.java
index 3c93e4406f0..ed3507e0201 100644
--- a/src/java.base/share/classes/java/lang/reflect/Constructor.java
+++ b/src/java.base/share/classes/java/lang/reflect/Constructor.java
@@ -26,6 +26,7 @@
 package java.lang.reflect;
 
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.ConstructorAccessor;
 import jdk.internal.reflect.Reflection;
@@ -63,19 +64,17 @@
  * @since 1.1
  */
 public final class Constructor<T> extends Executable {
-    @Stable
-    private Class<T>            clazz;
-    private int                 slot;
-    private Class<?>[]          parameterTypes;
-    private Class<?>[]          exceptionTypes;
-    @Stable
-    private int                 modifiers;
+    private final Class<T>            clazz;
+    private final int                 slot;
+    private final Class<?>[]          parameterTypes;
+    private final Class<?>[]          exceptionTypes;
+    private final int                 modifiers;
     // Generics and annotations support
-    private transient String    signature;
+    private final transient String    signature;
     // generic info repository; lazily initialized
     private transient ConstructorRepository genericInfo;
-    private byte[]              annotations;
-    private byte[]              parameterAnnotations;
+    private final byte[]              annotations;
+    private final byte[]              parameterAnnotations;
 
     // Generics infrastructure
     // Accessor for factory
@@ -493,9 +492,6 @@ T newInstanceWithCaller(Object[] args, boolean checkAccess, Class<?> caller)
         if (checkAccess)
             checkAccess(caller, clazz, clazz, modifiers);
 
-        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
-            throw new IllegalArgumentException("Cannot reflectively create enum objects");
-
         ConstructorAccessor ca = constructorAccessor;   // read @Stable
         if (ca == null) {
             ca = acquireConstructorAccessor();
@@ -535,6 +531,7 @@ public boolean isSynthetic() {
     // synchronization will probably make the implementation more
     // scalable.
     private ConstructorAccessor acquireConstructorAccessor() {
+
         // First check to see if one has been created yet, and take it
         // if so.
         Constructor<?> root = this.root;
@@ -543,8 +540,14 @@ private ConstructorAccessor acquireConstructorAccessor() {
             constructorAccessor = tmp;
         } else {
             // Otherwise fabricate one and propagate it up to the root
+            // Ensure the declaring class is not an Enum class.
+            if ((clazz.getModifiers() & Modifier.ENUM) != 0)
+                throw new IllegalArgumentException("Cannot reflectively create enum objects");
+
             tmp = reflectionFactory.newConstructorAccessor(this);
-            setConstructorAccessor(tmp);
+            // set the constructor accessor only if it's not using native implementation
+            if (VM.isJavaLangInvokeInited())
+                setConstructorAccessor(tmp);
         }
 
         return tmp;
diff --git a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java
index 23f72ed97a0..da4e970d4d7 100644
--- a/src/java.base/share/classes/java/lang/reflect/Field.java
+++ b/src/java.base/share/classes/java/lang/reflect/Field.java
@@ -65,23 +65,19 @@
  */
 public final
 class Field extends AccessibleObject implements Member {
-
-    @Stable
-    private Class<?>            clazz;
-    private int                 slot;
+    private final Class<?>            clazz;
+    private final int                 slot;
     // This is guaranteed to be interned by the VM in the 1.4
     // reflection implementation
-    private String              name;
-    @Stable
-    private Class<?>            type;
-    @Stable
-    private int                 modifiers;
-    private boolean             trustedFinal;
+    private final String              name;
+    private final Class<?>            type;
+    private final int                 modifiers;
+    private final boolean             trustedFinal;
     // Generics and annotations support
-    private transient String    signature;
+    private final transient String    signature;
     // generic info repository; lazily initialized
     private transient FieldRepository genericInfo;
-    private byte[]              annotations;
+    private final byte[]              annotations;
     // Cached field accessor created without override
     @Stable
     private FieldAccessor fieldAccessor;
diff --git a/src/java.base/share/classes/java/lang/reflect/Method.java b/src/java.base/share/classes/java/lang/reflect/Method.java
index d30a47f14c0..bd552d17ea4 100644
--- a/src/java.base/share/classes/java/lang/reflect/Method.java
+++ b/src/java.base/share/classes/java/lang/reflect/Method.java
@@ -26,7 +26,9 @@
 package java.lang.reflect;
 
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.MethodAccessor;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.vm.annotation.ForceInline;
@@ -67,24 +69,22 @@
  * @since 1.1
  */
 public final class Method extends Executable {
-    @Stable
-    private Class<?>            clazz;
-    private int                 slot;
+    private final Class<?>            clazz;
+    private final int                 slot;
     // This is guaranteed to be interned by the VM in the 1.4
     // reflection implementation
-    private String              name;
-    private Class<?>            returnType;
-    private Class<?>[]          parameterTypes;
-    private Class<?>[]          exceptionTypes;
-    @Stable
-    private int                 modifiers;
+    private final String              name;
+    private final Class<?>            returnType;
+    private final Class<?>[]          parameterTypes;
+    private final Class<?>[]          exceptionTypes;
+    private final int                 modifiers;
     // Generics and annotations support
-    private transient String              signature;
+    private final transient String    signature;
     // generic info repository; lazily initialized
     private transient MethodRepository genericInfo;
-    private byte[]              annotations;
-    private byte[]              parameterAnnotations;
-    private byte[]              annotationDefault;
+    private final byte[]              annotations;
+    private final byte[]              parameterAnnotations;
+    private final byte[]              annotationDefault;
     @Stable
     private MethodAccessor      methodAccessor;
     // For sharing of MethodAccessors. This branching structure is
@@ -554,20 +554,64 @@ void specificToGenericStringHeader(StringBuilder sb) {
     @ForceInline // to ensure Reflection.getCallerClass optimization
     @IntrinsicCandidate
     public Object invoke(Object obj, Object... args)
-        throws IllegalAccessException, IllegalArgumentException,
-           InvocationTargetException
+        throws IllegalAccessException, InvocationTargetException
     {
+        boolean callerSensitive = isCallerSensitive();
+        Class<?> caller = null;
+        if (!override || callerSensitive) {
+            caller = Reflection.getCallerClass();
+        }
+
+        // Reflection::getCallerClass filters all subclasses of
+        // jdk.internal.reflect.MethodAccessorImpl and Method::invoke(Object, Object[])
+        // Should not call Method::invoke(Object, Object[], Class) here
+        if (!override) {
+            checkAccess(caller, clazz,
+                    Modifier.isStatic(modifiers) ? null : obj.getClass(),
+                    modifiers);
+        }
+        MethodAccessor ma = methodAccessor;             // read @Stable
+        if (ma == null) {
+            ma = acquireMethodAccessor();
+        }
+
+        return callerSensitive ? ma.invoke(obj, args, caller) : ma.invoke(obj, args);
+    }
+
+    /**
+     * This is to support MethodHandle calling caller-sensitive Method::invoke
+     * that may invoke a caller-sensitive method in order to get the original caller
+     * class (not the injected invoker).
+     *
+     * If this adapter is not presented, MethodHandle invoking Method::invoke
+     * will get an invoker class, a hidden nestmate of the original caller class,
+     * that becomes the caller class invoking Method::invoke.
+     */
+    @CallerSensitiveAdapter
+    private Object invoke(Object obj, Object[] args, Class<?> caller)
+            throws IllegalAccessException, InvocationTargetException
+    {
+        boolean callerSensitive = isCallerSensitive();
         if (!override) {
-            Class<?> caller = Reflection.getCallerClass();
             checkAccess(caller, clazz,
                         Modifier.isStatic(modifiers) ? null : obj.getClass(),
                         modifiers);
         }
-        MethodAccessor ma = methodAccessor;             // read volatile
+        MethodAccessor ma = methodAccessor;             // read @Stable
         if (ma == null) {
             ma = acquireMethodAccessor();
         }
-        return ma.invoke(obj, args);
+
+        return callerSensitive ? ma.invoke(obj, args, caller) : ma.invoke(obj, args);
+    }
+
+    @Stable private Boolean callerSensitive;       // lazily initialize
+    private boolean isCallerSensitive() {
+        Boolean cs = callerSensitive;
+        if (cs == null) {
+            callerSensitive = cs = Reflection.isCallerSensitive(this);
+        }
+        return cs;
     }
 
     /**
@@ -673,8 +717,10 @@ private MethodAccessor acquireMethodAccessor() {
             methodAccessor = tmp;
         } else {
             // Otherwise fabricate one and propagate it up to the root
-            tmp = reflectionFactory.newMethodAccessor(this);
-            setMethodAccessor(tmp);
+            tmp = reflectionFactory.newMethodAccessor(this, isCallerSensitive());
+            // set the method accessor only if it's not using native implementation
+            if (VM.isJavaLangInvokeInited())
+                setMethodAccessor(tmp);
         }
 
         return tmp;
diff --git a/src/java.base/share/classes/java/lang/runtime/PrimitiveObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/PrimitiveObjectMethods.java
index 73cda62cbe0..6ad4f2fada6 100644
--- a/src/java.base/share/classes/java/lang/runtime/PrimitiveObjectMethods.java
+++ b/src/java.base/share/classes/java/lang/runtime/PrimitiveObjectMethods.java
@@ -71,7 +71,7 @@ static MethodHandle[] getters(Class<?> type, Comparator<MethodHandle> comparator
                 .filter(f -> !Modifier.isStatic(f.getModifiers()))
                 .map(f -> {
                     try {
-                        return JLIA.unreflectGetter(f);
+                        return JLIA.unreflectField(f, false);
                     } catch (IllegalAccessException e) {
                         throw newLinkageError(e);
                     }
@@ -260,7 +260,7 @@ private static MethodHandle findStatic(String name, MethodType methodType) {
         private static MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) {
             try {
                 return JLIA.findStatic(cls, name, methodType);
-            } catch (NoSuchMethodException|IllegalAccessException e) {
+            } catch (IllegalAccessException e) {
                 throw newLinkageError(e);
             }
         }
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
index e1818cd0522..ee655188c9d 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java
@@ -28,8 +28,10 @@
 import jdk.internal.invoke.NativeEntryPoint;
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.lang.invoke.VarHandle;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.nio.ByteOrder;
 import java.util.List;
@@ -141,7 +143,42 @@ VarHandle memoryAccessVarHandle(Class<?> carrier, boolean skipAlignmentMaskCheck
      */
     void ensureCustomized(MethodHandle mh);
 
-    MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException;
+    /**
+     * Produces a method handle unreflecting from a {@code Constructor} with
+     * the trusted lookup
+     */
+    MethodHandle unreflectConstructor(Constructor<?> ctor) throws IllegalAccessException;
+
+    /**
+     * Produces a method handle unreflecting from a {@code Field} with
+     * the trusted lookup
+     */
+    MethodHandle unreflectField(Field field, boolean isSetter) throws IllegalAccessException;
 
-    MethodHandle unreflectGetter(Field field) throws IllegalAccessException;
+    /**
+     * Produces a method handle of a virtual method with the trusted lookup.
+     */
+    MethodHandle findVirtual(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
+
+    /**
+     * Produces a method handle of a static method with the trusted lookup.
+     */
+    MethodHandle findStatic(Class<?> defc, String name, MethodType type) throws IllegalAccessException;
+
+    /**
+     * Returns a method handle of an invoker class injected for core reflection
+     * implementation with the following signature:
+     *     reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
+     *
+     * The invoker class is a hidden class which has the same
+     * defining class loader, runtime package, and protection domain
+     * as the given caller class.
+     */
+    MethodHandle reflectiveInvoker(Class<?> caller);
+
+    /**
+     * Defines a hidden class of the given name and bytes with class data.
+     * The given bytes is trusted.
+     */
+    Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize);
 }
diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java
index f6d3638c3dd..1e98e727b79 100644
--- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java
+++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java
@@ -51,7 +51,6 @@ interface and provides the ability to call package-private methods
     for this purpose, namely the loss of compile-time checking. */
 
 public class SharedSecrets {
-    private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
     private static JavaAWTAccess javaAWTAccess;
     private static JavaAWTFontAccess javaAWTFontAccess;
     private static JavaBeansAccess javaBeansAccess;
diff --git a/src/java.base/share/classes/jdk/internal/misc/VM.java b/src/java.base/share/classes/jdk/internal/misc/VM.java
index 5ad27d8e7ee..e8603ed46cf 100644
--- a/src/java.base/share/classes/jdk/internal/misc/VM.java
+++ b/src/java.base/share/classes/jdk/internal/misc/VM.java
@@ -27,14 +27,13 @@
 
 import static java.lang.Thread.State.*;
 
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import jdk.internal.access.SharedSecrets;
-
+import jdk.internal.vm.annotation.Stable;
 import sun.nio.ch.FileChannelImpl;
 
 public class VM {
@@ -91,7 +90,19 @@ public static void awaitInitLevel(int value) throws InterruptedException {
      * @see java.lang.System#initPhase2
      */
     public static boolean isModuleSystemInited() {
-        return VM.initLevel() >= MODULE_SYSTEM_INITED;
+        return initLevel >= MODULE_SYSTEM_INITED;
+    }
+
+    private static @Stable boolean javaLangInvokeInited;
+    public static void setJavaLangInvokeInited() {
+        if (javaLangInvokeInited) {
+            throw new InternalError("java.lang.invoke already inited");
+        }
+        javaLangInvokeInited = true;
+    }
+
+    public static boolean isJavaLangInvokeInited() {
+        return javaLangInvokeInited;
     }
 
     /**
diff --git a/src/java.base/share/classes/jdk/internal/reflect/AccessorUtils.java b/src/java.base/share/classes/jdk/internal/reflect/AccessorUtils.java
new file mode 100644
index 00000000000..64b928e05e0
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/AccessorUtils.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.WrongMethodTypeException;
+import java.util.Set;
+
+/**
+ * Utility methods used by DirectMethodHandleAccessor and DirectConstructorHandleAccessor
+ */
+public class AccessorUtils {
+    /**
+     * Determines if the given exception thrown by MethodHandle::invokeExact
+     * is caused by an illegal argument passed to Method::invoke or
+     * Constructor::newInstance.  This method inspects the stack trace of
+     * the exception to detect if it is thrown by the method handle core
+     * implementation or the implementation of the reflected method or constructor.
+     *
+     * MethodHandle::invoke throws ClassCastException if the receiver object
+     * is not an instance of the declaring class of the method if the method
+     * is an instance method, or if a parameter value cannot be converted
+     * to the corresponding formal parameter type.  It throws
+     * NullPointerException if the receiver object is null if the method
+     * is an instance method, or if unboxing operation of a parameter fails
+     * because the parameter value is null.  It throws WrongMethodTypeException
+     * if the method type mismatches.
+     *
+     * @param accessorType the accessor class that does the method handle invocation
+     * @param e ClassCastException, NullPointerException or WrongMethodTypeException
+     */
+    static boolean isIllegalArgument(Class<?> accessorType, RuntimeException e) {
+        assert(e instanceof ClassCastException || e instanceof NullPointerException ||
+               e instanceof WrongMethodTypeException);
+
+        StackTraceElement[] stackTrace = e.getStackTrace();
+        if (stackTrace.length == 0) {
+            return false;       // would this happen?
+        }
+
+        int i = 0;
+        StackTraceElement frame = stackTrace[0];
+        // Class::cast and Objects::requiresNonNull may be thrown by the implementation
+        // of the reflected method/constructor.  Skip them and continue.
+        if ((frame.getClassName().equals("java.lang.Class") && frame.getMethodName().equals("cast"))
+                || (frame.getClassName().equals("java.util.Objects") && frame.getMethodName().equals("requiresNonNull"))) {
+            i++;
+        }
+        for (; i < stackTrace.length; i++) {
+            frame = stackTrace[i];
+            String cname = frame.getClassName();
+            // it's illegal argument if this exception is thrown from accessorType
+            if (cname.equals(accessorType.getName())) {
+                return true;
+            }
+            // if this exception is thrown from an unnamed module or not from java.base
+            // then i.e. not from method handle core implementation
+            if (frame.getModuleName() == null || !frame.getModuleName().equals("java.base")) {
+                return false;
+            }
+            int index = cname.lastIndexOf(".");
+            String pn = index > 0 ? cname.substring(0, index) : "";
+            // exception thrown from java.base but not from core reflection/method handle internals
+            if (!IMPL_PACKAGES.contains(pn)) {
+                return false;
+            }
+            // If Constructor::newInstance is invoked by Method::invoke or vice versa,
+            // so the exception is thrown from the implementation body of the reflected
+            // method or constructor
+            if ((accessorType == DirectMethodHandleAccessor.class
+                    && cname.startsWith(DirectConstructorHandleAccessor.class.getName()))
+                || (accessorType == DirectConstructorHandleAccessor.class
+                        && cname.startsWith(DirectMethodHandleAccessor.class.getName()))) {
+                // thrown from another reflection accessor impl class
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private static final Set<String> IMPL_PACKAGES = Set.of(
+            "java.lang.reflect",
+            "java.lang.invoke",
+            "jdk.internal.reflect",
+            "sun.invoke.util"
+    );
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/CallerSensitiveAdapter.java b/src/java.base/share/classes/jdk/internal/reflect/CallerSensitiveAdapter.java
new file mode 100644
index 00000000000..2d7fced04d2
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/CallerSensitiveAdapter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+/**
+ * A method annotated @CallerSensitiveAdapter is an adapter method corresponding
+ * to a caller-sensitive method, which is annotated with @CallerSensitive.
+ *
+ * A caller-sensitive adapter is private and has the same name as its
+ * corresponding caller-sensitive method with a trailing caller class parameter.
+ *
+ * When a caller-sensitive method is invoked via Method::invoke or MethodHandle
+ * the core reflection and method handle implementation will find if
+ * an @CallerSensitiveAdapter method for that CSM is present. If present,
+ * the runtime will invoke the adapter method with the caller class
+ * argument instead. This special calling sequence ensures that the same caller
+ * class is passed to a caller-sensitive method via Method::invoke,
+ * MethodHandle::invokeExact, or a mix of these methods.
+ *
+ * For example, CSM::returnCallerClass is a caller-sensitive method
+ * with an adapter method:
+ * {@code
+ * class CSM {
+ *     @CallerSensitive
+ *     static Class<?> returnCallerClass() {
+ *         return returnCallerClass(Reflection.getCallerClass());
+ *     }
+ *     @CallerSensitiveAdapter
+ *     private static Class<?> returnCallerClass(Class<?> caller) {
+ *         return caller;
+ *     }
+ * }
+ *
+ * class Test {
+ *     void test() throws Throwable {
+ *         // calling CSM::returnCallerClass via reflection
+ *         var csm = CSM.class.getMethod("returnCallerClass");
+ *         // expect Foo to be the caller class
+ *         var caller = csm.invoke(null);
+ *         assert(caller == Test.class);
+ *     }
+ *     void test2() throws Throwable {
+ *         // method handle for Method::invoke
+ *         MethodHandle mh = MethodHandles.lookup().findVirtual(Method.class, "invoke",
+ *                                  methodType(Object.class, Object.class, Object[].class));
+ *         var csm = CSM.class.getMethod("returnCallerClass");
+ *         // invoke Method::invoke via method handle and the target method
+ *         // being invoked reflectively is CSM::returnCallerClass
+ *         var caller = mh.invoke(csm, null, null);
+ *         assert(caller == Test.class);
+ *     }
+ * }
+ * }
+ *
+ *
+ * Both CSM::returnCallerClass and Method::invoke can have an adapter method
+ * with a caller-class parameter defined. Test::test calls CSM::returnCallerClass
+ * via Method::invoke which does the stack walking to find the caller's class.
+ * It will pass the caller's class directly to the adapter method for
+ * CSM::returnCallerClass.
+ *
+ * Similarly, Test::test2 invokes the method handle of Method::invoke to
+ * call CSM::returnCallerClass reflectively.  In that case, MethodHandle::invokeExact
+ * uses the lookup class of the Lookup object producing the method handle
+ * as the caller's class, so no stack walking involved. The lookup class is Test.
+ * It will invoke the adapter method of Method::invoke with Test as the caller's
+ * class, which in turn invokes the adapter method of CSM::returnCallerClass
+ * with Test as the caller. The calling sequence eliminates the need for
+ * multiple stack walks when a caller-sensitive method is invoked reflectively.
+ *
+ * For caller-sensitive methods that require an exact caller class, the adapter
+ * method must be defined for correctness. {@link java.lang.invoke.MethodHandles#lookup()}
+ * and {@link ClassLoader#registerAsParallelCapable()} are the only two methods
+ * in the JDK that need the exact caller class.
+ *
+ * On the other hand, for caller-sensitive methods that use the caller's class
+ * for access checks or security permission checks, i.e., based on its runtime
+ * package, defining loader, or protection domain, the adapter method is optional.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({METHOD})
+public @interface CallerSensitiveAdapter {
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/CsMethodAccessorAdapter.java b/src/java.base/share/classes/jdk/internal/reflect/CsMethodAccessorAdapter.java
new file mode 100644
index 00000000000..a2cfae6e975
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/CsMethodAccessorAdapter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package jdk.internal.reflect;
+
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.Hidden;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * MethodAccessor adapter for caller-sensitive methods which have
+ * an alternate non-CSM method with the same method name but an additional
+ * caller class argument.
+ *
+ * When a caller-sensitive method is called,
+ * Method::invoke(Object target, Object[] args, Class<?> caller) will
+ * be invoked with the caller class.  If an adapter is present,
+ * the adapter method with the caller class parameter will be called
+ * instead.
+ */
+class CsMethodAccessorAdapter extends MethodAccessorImpl {
+    private final Method csmAdapter;
+    private final MethodAccessor accessor;
+
+    CsMethodAccessorAdapter(Method method, Method csmAdapter, MethodAccessor accessor) {
+        assert Reflection.isCallerSensitive(method) && !Reflection.isCallerSensitive(csmAdapter);
+        this.csmAdapter = csmAdapter;
+        this.accessor = accessor;
+    }
+
+    @Override
+    public Object invoke(Object obj, Object[] args)
+            throws IllegalArgumentException, InvocationTargetException {
+        throw new InternalError("caller-sensitive method invoked without explicit caller: " + csmAdapter);
+    }
+
+    @Override
+    @ForceInline
+    @Hidden
+    public Object invoke(Object obj, Object[] args, Class<?> caller)
+            throws IllegalArgumentException, InvocationTargetException {
+        Object[] newArgs = new Object[args == null ? 1 : args.length + 1];
+        newArgs[0] = caller;
+        if (args != null) {
+            System.arraycopy(args, 0, newArgs, 1, args.length);
+        }
+        return accessor.invoke(obj, newArgs);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java
index eaa9a786392..d8c525b8958 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/DelegatingConstructorAccessorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2021, 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
@@ -35,7 +35,6 @@
 
 class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl {
     // initial non-null delegate
-    @Stable
     private final ConstructorAccessorImpl initialDelegate;
     // alternative delegate: starts as null;
     // only single change from null -> non-null is guaranteed
diff --git a/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java
index 09cb4159174..d10cceccf7a 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/DelegatingMethodAccessorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2021, 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
@@ -35,10 +35,11 @@
 
 class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
     // initial non-null delegate
-    @Stable private final MethodAccessorImpl initialDelegate;
+    private final MethodAccessorImpl initialDelegate;
     // alternative delegate: starts as null;
     // only single change from null -> non-null is guaranteed
-    @Stable private  MethodAccessorImpl altDelegate;
+    @Stable
+    private MethodAccessorImpl altDelegate;
 
     DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
         initialDelegate = Objects.requireNonNull(delegate);
@@ -51,6 +52,13 @@ public Object invoke(Object obj, Object[] args)
         return delegate().invoke(obj, args);
     }
 
+    @Override
+    public Object invoke(Object obj, Object[] args, Class<?> caller)
+            throws IllegalArgumentException, InvocationTargetException
+    {
+        return delegate().invoke(obj, args, caller);
+    }
+
     private MethodAccessorImpl delegate() {
         var d = altDelegate;
         return  d != null ? d : initialDelegate;
diff --git a/src/java.base/share/classes/jdk/internal/reflect/DirectConstructorHandleAccessor.java b/src/java.base/share/classes/jdk/internal/reflect/DirectConstructorHandleAccessor.java
new file mode 100644
index 00000000000..4c0f7bdabb0
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/DirectConstructorHandleAccessor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.Hidden;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
+
+class DirectConstructorHandleAccessor extends ConstructorAccessorImpl {
+    static ConstructorAccessorImpl constructorAccessor(Constructor<?> ctor, MethodHandle target) {
+        return new DirectConstructorHandleAccessor(ctor, target);
+    }
+
+    static ConstructorAccessorImpl nativeAccessor(Constructor<?> ctor) {
+        return new NativeAccessor(ctor);
+    }
+
+    private static final int PARAM_COUNT_MASK = 0x00FF;
+    private static final int NONZERO_BIT = 0x8000_0000;
+
+    private final int paramFlags;
+    private final MethodHandle target;
+
+    DirectConstructorHandleAccessor(Constructor<?> ctor, MethodHandle target) {
+        this.paramFlags = (ctor.getParameterCount() & PARAM_COUNT_MASK) | NONZERO_BIT;
+        this.target = target;
+    }
+
+    @Override
+    public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
+        int argc = args != null ? args.length : 0;
+        // only check argument count for specialized forms
+        int paramCount = paramFlags & PARAM_COUNT_MASK;
+        if (paramCount <= SPECIALIZED_PARAM_COUNT && argc != paramCount) {
+            throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
+        }
+        try {
+            return invokeImpl(args);
+        } catch (ClassCastException|WrongMethodTypeException e) {
+            if (isIllegalArgument(e))
+                throw new IllegalArgumentException("argument type mismatch", e);
+            else
+                throw new InvocationTargetException(e);
+        } catch (NullPointerException e) {
+            if (isIllegalArgument(e))
+                throw new IllegalArgumentException(e);
+            else
+                throw new InvocationTargetException(e);
+        } catch (Throwable e) {
+            throw new InvocationTargetException(e);
+        }
+    }
+
+    private boolean isIllegalArgument(RuntimeException ex) {
+        return AccessorUtils.isIllegalArgument(DirectConstructorHandleAccessor.class, ex);
+    }
+
+    @Hidden
+    @ForceInline
+    Object invokeImpl(Object[] args) throws Throwable {
+        return switch (paramFlags & PARAM_COUNT_MASK) {
+            case 0 -> target.invokeExact();
+            case 1 -> target.invokeExact(args[0]);
+            case 2 -> target.invokeExact(args[0], args[1]);
+            case 3 -> target.invokeExact(args[0], args[1], args[2]);
+            default -> target.invokeExact(args);
+        };
+    }
+
+    /**
+     * Invoke the constructor via native VM reflection
+     */
+    static class NativeAccessor extends ConstructorAccessorImpl {
+        private final Constructor<?> ctor;
+        NativeAccessor(Constructor<?> ctor) {
+            this.ctor = ctor;
+        }
+
+        @Override
+        public Object newInstance(Object[] args) throws InstantiationException, InvocationTargetException {
+            return newInstance0(ctor, args);
+        }
+        private static native Object newInstance0(Constructor<?> c, Object[] args)
+                    throws InstantiationException, InvocationTargetException;
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java
new file mode 100644
index 00000000000..86609cc2727
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import jdk.internal.access.JavaLangInvokeAccess;
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
+import jdk.internal.vm.annotation.ForceInline;
+import jdk.internal.vm.annotation.Hidden;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import static java.lang.invoke.MethodType.genericMethodType;
+import static jdk.internal.reflect.MethodHandleAccessorFactory.SPECIALIZED_PARAM_COUNT;
+import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.JLIA;
+
+class DirectMethodHandleAccessor extends MethodAccessorImpl {
+    /**
+     * Creates a MethodAccessorImpl for a non-native method.
+     */
+    static MethodAccessorImpl methodAccessor(Method method, MethodHandle target) {
+        assert !Modifier.isNative(method.getModifiers());
+
+        return new DirectMethodHandleAccessor(method, target, false);
+    }
+
+    /**
+     * Creates MethodAccessorImpl for the adapter method for a caller-sensitive method.
+     * The given target method handle is the adapter method with the additional caller class
+     * parameter.
+     */
+    static MethodAccessorImpl callerSensitiveAdapter(Method original, MethodHandle target) {
+        assert Reflection.isCallerSensitive(original);
+
+        // for CSM adapter method with the additional caller class parameter
+        // creates the adaptive method accessor only.
+        return new DirectMethodHandleAccessor(original, target, true);
+    }
+
+    /**
+     * Creates MethodAccessorImpl that invokes the given method via VM native reflection
+     * support.  This is used for native methods.  It can be used for java methods
+     * during early VM startup.
+     */
+    static MethodAccessorImpl nativeAccessor(Method method, boolean callerSensitive) {
+        return callerSensitive ? new NativeAccessor(method, findCSMethodAdapter(method))
+                               : new NativeAccessor(method);
+    }
+
+    private static final int PARAM_COUNT_MASK = 0x00FF;
+    private static final int HAS_CALLER_PARAM_BIT = 0x0100;
+    private static final int IS_STATIC_BIT = 0x0200;
+    private static final int NONZERO_BIT = 0x8000_0000;
+
+    private final Class<?> declaringClass;
+    private final int paramCount;
+    private final int flags;
+    private final MethodHandle target;
+
+    DirectMethodHandleAccessor(Method method, MethodHandle target, boolean hasCallerParameter) {
+        this.declaringClass = method.getDeclaringClass();
+        this.paramCount = method.getParameterCount();
+        this.flags = (hasCallerParameter ? HAS_CALLER_PARAM_BIT : 0) |
+                     (Modifier.isStatic(method.getModifiers()) ? IS_STATIC_BIT : 0);
+        this.target = target;
+    }
+
+    @Override
+    @ForceInline
+    public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
+        if (!isStatic()) {
+            checkReceiver(obj);
+        }
+        checkArgumentCount(paramCount, args);
+        try {
+            return invokeImpl(obj, args);
+        } catch (ClassCastException | WrongMethodTypeException e) {
+            if (isIllegalArgument(e)) {
+                // No cause in IAE to be consistent with the old behavior
+                throw new IllegalArgumentException("argument type mismatch");
+            } else {
+                throw new InvocationTargetException(e);
+            }
+        } catch (NullPointerException e) {
+            if (isIllegalArgument(e)) {
+                throw new IllegalArgumentException(e);
+            } else {
+                throw new InvocationTargetException(e);
+            }
+        } catch (Throwable e) {
+            throw new InvocationTargetException(e);
+        }
+    }
+
+    @Override
+    @ForceInline
+    public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
+        if (!isStatic()) {
+            checkReceiver(obj);
+        }
+        checkArgumentCount(paramCount, args);
+        try {
+            return invokeImpl(obj, args, caller);
+        } catch (ClassCastException | WrongMethodTypeException e) {
+            if (isIllegalArgument(e)) {
+                // No cause in IAE to be consistent with the old behavior
+                throw new IllegalArgumentException("argument type mismatch");
+            } else {
+                throw new InvocationTargetException(e);
+            }
+        } catch (NullPointerException e) {
+            if (isIllegalArgument(e)) {
+                throw new IllegalArgumentException(e);
+            } else {
+                throw new InvocationTargetException(e);
+            }
+        } catch (Throwable e) {
+            throw new InvocationTargetException(e);
+        }
+    }
+
+    @Hidden
+    @ForceInline
+    private Object invokeImpl(Object obj, Object[] args) throws Throwable {
+        return switch (paramCount) {
+            case 0 -> target.invokeExact(obj);
+            case 1 -> target.invokeExact(obj, args[0]);
+            case 2 -> target.invokeExact(obj, args[0], args[1]);
+            case 3 -> target.invokeExact(obj, args[0], args[1], args[2]);
+            default -> target.invokeExact(obj, args);
+        };
+    }
+
+    @Hidden
+    @ForceInline
+    private Object invokeImpl(Object obj, Object[] args, Class<?> caller) throws Throwable {
+        if (hasCallerParameter()) {
+            // caller-sensitive method is invoked through method with caller parameter
+            return switch (paramCount) {
+                case 0 -> target.invokeExact(obj, caller);
+                case 1 -> target.invokeExact(obj, args[0], caller);
+                case 2 -> target.invokeExact(obj, args[0], args[1], caller);
+                case 3 -> target.invokeExact(obj, args[0], args[1], args[2], caller);
+                default -> target.invokeExact(obj, args, caller);
+            };
+        } else {
+            // caller-sensitive method is invoked through a per-caller invoker while
+            // the target MH is always spreading the args
+            var invoker = JLIA.reflectiveInvoker(caller);
+            try {
+                // invoke the target method handle via an invoker
+                return invoker.invokeExact(target, obj, args);
+            } catch (IllegalArgumentException e) {
+                throw new InvocationTargetException(e);
+            }
+        }
+    }
+
+    private boolean isStatic() {
+        return (flags & IS_STATIC_BIT) == IS_STATIC_BIT;
+    }
+
+    private boolean hasCallerParameter() {
+        return (flags & HAS_CALLER_PARAM_BIT) == HAS_CALLER_PARAM_BIT;
+    }
+
+    private boolean isIllegalArgument(RuntimeException ex) {
+        return AccessorUtils.isIllegalArgument(DirectMethodHandleAccessor.class, ex);
+    }
+
+    private void checkReceiver(Object o) {
+        // NOTE: will throw NullPointerException, as specified, if o is null
+        if (!declaringClass.isAssignableFrom(o.getClass())) {
+            throw new IllegalArgumentException("object is not an instance of declaring class");
+        }
+    }
+
+    /**
+     * Invoke the method via native VM reflection
+     */
+    static class NativeAccessor extends MethodAccessorImpl {
+        private final Method method;
+        private final Method csmAdapter;
+        private final boolean callerSensitive;
+        NativeAccessor(Method method) {
+            assert !Reflection.isCallerSensitive(method);
+            this.method = method;
+            this.csmAdapter = null;
+            this.callerSensitive = false;
+        }
+
+        NativeAccessor(Method method, Method csmAdapter) {
+            assert Reflection.isCallerSensitive(method);
+            this.method = method;
+            this.csmAdapter = csmAdapter;
+            this.callerSensitive = true;
+        }
+
+        @Override
+        public Object invoke(Object obj, Object[] args) throws InvocationTargetException {
+            assert csmAdapter == null;
+            return invoke0(method, obj, args);
+        }
+
+        @Override
+        public Object invoke(Object obj, Object[] args, Class<?> caller) throws InvocationTargetException {
+            assert callerSensitive;
+
+            if (csmAdapter != null) {
+                Object[] newArgs = new Object[csmAdapter.getParameterCount()];
+                newArgs[0] = caller;
+                if (args != null) {
+                    System.arraycopy(args, 0, newArgs, 1, args.length);
+                }
+                return invoke0(csmAdapter, obj, newArgs);
+            } else {
+                assert VM.isJavaLangInvokeInited();
+                try {
+                    return ReflectiveInvoker.invoke(methodAccessorInvoker(), caller, obj, args);
+                } catch (InvocationTargetException|RuntimeException|Error e) {
+                    throw e;
+                } catch (Throwable e) {
+                    throw new InternalError(e);
+                }
+            }
+        }
+
+        public Object invokeViaReflectiveInvoker(Object obj, Object[] args) throws InvocationTargetException {
+            return invoke0(method, obj, args);
+        }
+
+        /*
+         * A method handle to invoke Reflective::Invoker
+         */
+        private MethodHandle maInvoker;
+        private MethodHandle methodAccessorInvoker() {
+            MethodHandle invoker = maInvoker;
+            if (invoker == null) {
+                maInvoker = invoker = ReflectiveInvoker.bindTo(this);
+            }
+            return invoker;
+        }
+
+        private static native Object invoke0(Method m, Object obj, Object[] args);
+
+        static class ReflectiveInvoker {
+            /**
+             * Return a method handle for NativeAccessor::invoke bound to the given accessor object
+             */
+            static MethodHandle bindTo(NativeAccessor accessor) {
+                return NATIVE_ACCESSOR_INVOKE.bindTo(accessor);
+            }
+
+            /*
+             * When Method::invoke on a caller-sensitive method is to be invoked
+             * and no adapter method with an additional caller class argument is defined,
+             * the caller-sensitive method must be invoked via an invoker injected
+             * which has the following signature:
+             *     reflect_invoke_V(MethodHandle mh, Object target, Object[] args)
+             *
+             * The stack frames calling the method `csm` through reflection will
+             * look like this:
+             *     obj.csm(args)
+             *     NativeAccessor::invoke(obj, args)
+             *     InjectedInvoker::reflect_invoke_V(vamh, obj, args);
+             *     method::invoke(obj, args)
+             *     p.Foo::m
+             *
+             * An injected invoker class is a hidden class which has the same
+             * defining class loader, runtime package, and protection domain
+             * as the given caller class.
+             *
+             * The caller-sensitive method will call Reflection::getCallerClass
+             * to get the caller class.
+             */
+            static Object invoke(MethodHandle target, Class<?> caller, Object obj, Object[] args)
+                    throws InvocationTargetException
+            {
+                var reflectInvoker = JLIA.reflectiveInvoker(caller);
+                try {
+                    return reflectInvoker.invokeExact(target, obj, args);
+                } catch (InvocationTargetException | RuntimeException | Error e) {
+                    throw e;
+                } catch (Throwable e) {
+                    throw new InternalError(e);
+                }
+            }
+
+            static final JavaLangInvokeAccess JLIA;
+            static final MethodHandle NATIVE_ACCESSOR_INVOKE;
+            static {
+                try {
+                    JLIA = SharedSecrets.getJavaLangInvokeAccess();
+                    NATIVE_ACCESSOR_INVOKE = MethodHandles.lookup().findVirtual(NativeAccessor.class, "invoke",
+                            genericMethodType(1, true));
+                } catch (NoSuchMethodException|IllegalAccessException e) {
+                    throw new InternalError(e);
+                }
+            }
+        }
+    }
+
+    private static void checkArgumentCount(int paramCount, Object[] args) {
+        // only check argument count for specialized forms
+        if (paramCount > SPECIALIZED_PARAM_COUNT) return;
+
+        int argc = args != null ? args.length : 0;
+        if (argc != paramCount) {
+            throw new IllegalArgumentException("wrong number of arguments: " + argc + " expected: " + paramCount);
+        }
+    }
+
+    /**
+     * Returns an adapter for caller-sensitive method if present.
+     * Otherwise, null.
+     *
+     * A trusted method can define an adapter method for a caller-sensitive method `foo`
+     * with an additional caller class argument that will be invoked reflectively.
+     */
+    private static Method findCSMethodAdapter(Method method) {
+        if (!Reflection.isCallerSensitive(method)) return null;
+
+        int paramCount = method.getParameterCount();
+        Class<?>[] ptypes = new Class<?>[paramCount+1];
+        ptypes[paramCount] = Class.class;
+        System.arraycopy(method.getParameterTypes(), 0, ptypes, 0, paramCount);
+        try {
+            return method.getDeclaringClass().getDeclaredMethod(method.getName(), ptypes);
+        } catch (NoSuchMethodException ex) {
+            return null;
+        }
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java
index b703d082534..cba836c5edd 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/FieldAccessorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2021, 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
@@ -25,9 +25,8 @@
 
 package jdk.internal.reflect;
 
-import jdk.internal.vm.annotation.Stable;
-
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 
 /** Package-private implementation of the FieldAccessor interface
     which has access to all classes and all fields, regardless of
@@ -35,7 +34,6 @@
 
 abstract class FieldAccessorImpl extends MagicAccessorImpl
     implements FieldAccessor {
-    @Stable
     protected final Field field;
 
     FieldAccessorImpl(Field field) {
@@ -113,4 +111,167 @@ public abstract void setFloat(Object obj, float f)
     /** Matches specification in {@link java.lang.reflect.Field} */
     public abstract void setDouble(Object obj, double d)
         throws IllegalArgumentException, IllegalAccessException;
+
+
+    protected void ensureObj(Object o) {
+        // NOTE: will throw NullPointerException, as specified, if o is null
+        if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
+            throwSetIllegalArgumentException(o);
+        }
+    }
+
+    private String getQualifiedFieldName() {
+        return field.getDeclaringClass().getName() + "." +field.getName();
+    }
+
+    protected IllegalArgumentException newGetIllegalArgumentException(String type) {
+        return new IllegalArgumentException(
+                "Attempt to get "+field.getType().getName()+" field \"" +
+                        getQualifiedFieldName() + "\" with illegal data type conversion to "+type
+        );
+    }
+
+    protected void throwFinalFieldIllegalAccessException(String attemptedType,
+                                                         String attemptedValue)
+            throws IllegalAccessException {
+        throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
+    }
+
+    protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("char", Character.toString(b));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("short", Short.toString(b));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("int", Integer.toString(i));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("long", Long.toString(i));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("float", Float.toString(f));
+    }
+
+    protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
+        throwFinalFieldIllegalAccessException("double", Double.toString(f));
+    }
+
+    protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
+        return newGetIllegalArgumentException("boolean");
+    }
+
+    protected IllegalArgumentException newGetByteIllegalArgumentException() {
+        return newGetIllegalArgumentException("byte");
+    }
+
+    protected IllegalArgumentException newGetCharIllegalArgumentException() {
+        return newGetIllegalArgumentException("char");
+    }
+
+    protected IllegalArgumentException newGetShortIllegalArgumentException() {
+        return newGetIllegalArgumentException("short");
+    }
+
+    protected IllegalArgumentException newGetIntIllegalArgumentException() {
+        return newGetIllegalArgumentException("int");
+    }
+
+    protected IllegalArgumentException newGetLongIllegalArgumentException() {
+        return newGetIllegalArgumentException("long");
+    }
+
+    protected IllegalArgumentException newGetFloatIllegalArgumentException() {
+        return newGetIllegalArgumentException("float");
+    }
+
+    protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
+        return newGetIllegalArgumentException("double");
+    }
+
+    protected String getSetMessage(String attemptedType, String attemptedValue) {
+        String err = "Can not set";
+        if (Modifier.isStatic(field.getModifiers()))
+            err += " static";
+        if (Modifier.isFinal(field.getModifiers()))
+            err += " final";
+        err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
+        if (!attemptedValue.isEmpty()) {
+            err += "(" + attemptedType + ")" + attemptedValue;
+        } else {
+            if (!attemptedType.isEmpty())
+                err += attemptedType;
+            else
+                err += "null value";
+        }
+        return err;
+    }
+
+    protected String getMessage(boolean getter, String attemptedType) {
+        String err = "Can not " + (getter ? "get" : "set");
+        if (Modifier.isStatic(field.getModifiers()))
+            err += " static";
+        if (Modifier.isFinal(field.getModifiers()))
+            err += " final";
+        err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " on " + attemptedType;
+        return err;
+    }
+
+    protected void throwSetIllegalArgumentException(String attemptedType,
+                                                    String attemptedValue) {
+        throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
+    }
+
+    protected void throwSetIllegalArgumentException(Object o) {
+        throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
+    }
+
+    protected void throwSetIllegalArgumentException(boolean b) {
+        throwSetIllegalArgumentException("boolean", Boolean.toString(b));
+    }
+
+    protected void throwSetIllegalArgumentException(byte b) {
+        throwSetIllegalArgumentException("byte", Byte.toString(b));
+    }
+
+    protected void throwSetIllegalArgumentException(char c) {
+        throwSetIllegalArgumentException("char", Character.toString(c));
+    }
+
+    protected void throwSetIllegalArgumentException(short s) {
+        throwSetIllegalArgumentException("short", Short.toString(s));
+    }
+
+    protected void throwSetIllegalArgumentException(int i) {
+        throwSetIllegalArgumentException("int", Integer.toString(i));
+    }
+
+    protected void throwSetIllegalArgumentException(long l) {
+        throwSetIllegalArgumentException("long", Long.toString(l));
+    }
+
+    protected void throwSetIllegalArgumentException(float f) {
+        throwSetIllegalArgumentException("float", Float.toString(f));
+    }
+
+    protected void throwSetIllegalArgumentException(double d) {
+        throwSetIllegalArgumentException("double", Double.toString(d));
+    }
+
 }
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessor.java b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessor.java
index 8ffc0233a96..27dfe4452e4 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessor.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessor.java
@@ -37,4 +37,7 @@ public interface MethodAccessor {
     /** Matches specification in {@link java.lang.reflect.Method} */
     public Object invoke(Object obj, Object[] args)
         throws IllegalArgumentException, InvocationTargetException;
+
+    public Object invoke(Object obj, Object[] args, Class<?> caller)
+            throws IllegalArgumentException, InvocationTargetException;
 }
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java
index 56422b4561a..40730c5356b 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorGenerator.java
@@ -28,8 +28,8 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-/** Generator for sun.reflect.MethodAccessor and
-    sun.reflect.ConstructorAccessor objects using bytecodes to
+/** Generator for jdk.internal.reflect.MethodAccessor and
+    jdk.internal.reflect.ConstructorAccessor objects using bytecodes to
     implement reflection. A java.lang.reflect.Method or
     java.lang.reflect.Constructor object can delegate its invoke or
     newInstance method to an accessor using native code or to one
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorImpl.java
index 91a02645f4e..1ef40d207f0 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodAccessorImpl.java
@@ -45,4 +45,9 @@ abstract class MethodAccessorImpl extends MagicAccessorImpl
     /** Matches specification in {@link java.lang.reflect.Method} */
     public abstract Object invoke(Object obj, Object[] args)
         throws IllegalArgumentException, InvocationTargetException;
+
+    public Object invoke(Object obj, Object[] args, Class<?> caller)
+            throws IllegalArgumentException, InvocationTargetException {
+        return invoke(obj, args);
+    }
 }
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java
new file mode 100644
index 00000000000..20ddd16d35a
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import jdk.internal.access.JavaLangInvokeAccess;
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.Unsafe;
+import jdk.internal.misc.VM;
+
+import static java.lang.invoke.MethodType.genericMethodType;
+import static java.lang.invoke.MethodType.methodType;
+import static jdk.internal.reflect.MethodHandleAccessorFactory.LazyStaticHolder.*;
+
+final class MethodHandleAccessorFactory {
+    /**
+     * Creates a MethodAccessor for the given reflected method.
+     *
+     * If the given method is called before the java.lang.invoke initialization
+     * or the given method is a native method, it will use the native VM reflection
+     * support.
+     *
+     * If the given method is a caller-sensitive method and the corresponding
+     * caller-sensitive adapter with the caller class parameter is present,
+     * it will use the method handle of the caller-sensitive adapter.
+     *
+     * Otherwise, it will use the direct method handle of the given method.
+     *
+     * @see CallerSensitive
+     * @see CallerSensitiveAdapter
+     */
+    static MethodAccessorImpl newMethodAccessor(Method method, boolean callerSensitive) {
+        if (useNativeAccessor(method)) {
+            return DirectMethodHandleAccessor.nativeAccessor(method, callerSensitive);
+        }
+
+        // ExceptionInInitializerError may be thrown during class initialization
+        // Ensure class initialized outside the invocation of method handle
+        // so that EIIE is propagated (not wrapped with ITE)
+        ensureClassInitialized(method.getDeclaringClass());
+
+        try {
+            if (callerSensitive) {
+                var dmh = findCallerSensitiveAdapter(method);
+                if (dmh != null) {
+                    return DirectMethodHandleAccessor.callerSensitiveAdapter(method, dmh);
+                }
+            }
+            var dmh = getDirectMethod(method, callerSensitive);
+            return DirectMethodHandleAccessor.methodAccessor(method, dmh);
+        } catch (IllegalAccessException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    /**
+     * Creates a ConstructorAccessor for the given reflected constructor.
+     *
+     * If a given constructor is called before the java.lang.invoke initialization,
+     * it will use the native VM reflection support.
+     *
+     * Otherwise, it will use the direct method handle of the given constructor.
+     */
+    static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
+        if (useNativeAccessor(ctor)) {
+            return DirectConstructorHandleAccessor.nativeAccessor(ctor);
+        }
+
+        // ExceptionInInitializerError may be thrown during class initialization
+        // Ensure class initialized outside the invocation of method handle
+        // so that EIIE is propagated (not wrapped with ITE)
+        ensureClassInitialized(ctor.getDeclaringClass());
+
+        try {
+            MethodHandle mh = JLIA.unreflectConstructor(ctor);
+            int paramCount = mh.type().parameterCount();
+            MethodHandle target = mh.asFixedArity();
+            MethodType mtype = specializedMethodTypeForConstructor(paramCount);
+            if (paramCount > SPECIALIZED_PARAM_COUNT) {
+                // spread the parameters only for the non-specialized case
+                target = target.asSpreader(Object[].class, paramCount);
+            }
+            target = target.asType(mtype);
+            return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
+        } catch (IllegalAccessException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    /**
+     * Creates a FieldAccessor for the given reflected field.
+     *
+     * Limitation: Field access via core reflection is only supported after
+     * java.lang.invoke completes initialization.
+     * java.lang.invoke initialization starts soon after System::initPhase1
+     * and method handles are ready for use when initPhase2 begins.
+     * During early VM startup (initPhase1), fields can be accessed directly
+     * from the VM or through JNI.
+     */
+    static FieldAccessorImpl newFieldAccessor(Field field, boolean isReadOnly) {
+        if (!VM.isJavaLangInvokeInited()) {
+            throw new InternalError(field.getDeclaringClass().getName() + "::" + field.getName() +
+                    " cannot be accessed reflectively before java.lang.invoke is initialized");
+        }
+
+        // ExceptionInInitializerError may be thrown during class initialization
+        // Ensure class initialized outside the invocation of method handle
+        // so that EIIE is propagated (not wrapped with ITE)
+        ensureClassInitialized(field.getDeclaringClass());
+
+        try {
+            // the declaring class of the field has been initialized
+            var getter = JLIA.unreflectField(field, false);
+            var setter = isReadOnly ? null : JLIA.unreflectField(field, true);
+            Class<?> type = field.getType();
+            if (type == Boolean.TYPE) {
+                return MethodHandleBooleanFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Byte.TYPE) {
+                return MethodHandleByteFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Short.TYPE) {
+                return MethodHandleShortFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Character.TYPE) {
+                return MethodHandleCharacterFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Integer.TYPE) {
+                return MethodHandleIntegerFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Long.TYPE) {
+                return MethodHandleLongFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Float.TYPE) {
+                return MethodHandleFloatFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else if (type == Double.TYPE) {
+                return MethodHandleDoubleFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            } else {
+                return MethodHandleObjectFieldAccessorImpl.fieldAccessor(field, getter, setter, isReadOnly);
+            }
+        } catch (IllegalAccessException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    private static MethodHandle getDirectMethod(Method method, boolean callerSensitive) throws IllegalAccessException {
+        var mtype = methodType(method.getReturnType(), method.getParameterTypes());
+        var isStatic = Modifier.isStatic(method.getModifiers());
+        var dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), method.getName(), mtype)
+                                        : JLIA.findVirtual(method.getDeclaringClass(), method.getName(), mtype);
+        if (callerSensitive) {
+            // the reflectiveInvoker for caller-sensitive method expects the same signature
+            // as Method::invoke i.e. (Object, Object[])Object
+            return makeTarget(dmh, isStatic, false);
+        }
+        return makeSpecializedTarget(dmh, isStatic, false);
+    }
+
+    /**
+     * Finds the method handle of a caller-sensitive adapter for the given
+     * caller-sensitive method.  It has the same name as the given method
+     * with a trailing caller class parameter.
+     *
+     * @see CallerSensitiveAdapter
+     */
+    private static MethodHandle findCallerSensitiveAdapter(Method method) throws IllegalAccessException {
+        String name = method.getName();
+        // append a Class parameter
+        MethodType mtype = methodType(method.getReturnType(), method.getParameterTypes())
+                                .appendParameterTypes(Class.class);
+        boolean isStatic = Modifier.isStatic(method.getModifiers());
+
+        MethodHandle dmh = isStatic ? JLIA.findStatic(method.getDeclaringClass(), name, mtype)
+                                    : JLIA.findVirtual(method.getDeclaringClass(), name, mtype);
+        return dmh != null ? makeSpecializedTarget(dmh, isStatic, true) : null;
+    }
+
+    /**
+     * Transform the given dmh to a specialized target method handle.
+     *
+     * If {@code hasCallerParameter} parameter is true, transform the method handle
+     * of this method type: {@code (Object, Object[], Class)Object} for the default
+     * case.
+     *
+     * If {@code hasCallerParameter} parameter is false, transform the method handle
+     * of this method type: {@code (Object, Object[])Object} for the default case.
+     *
+     * If the number of formal arguments is small, use a method type specialized
+     * the number of formal arguments is 0, 1, and 2, for example, the method type
+     * of a static method with one argument can be: {@code (Object)Object}
+     *
+     * If it's a static method, there is no leading Object parameter.
+     *
+     * @apiNote
+     * This implementation avoids using MethodHandles::catchException to help
+     * cold startup performance since this combination is very costly to setup.
+     *
+     * @param dmh DirectMethodHandle
+     * @param isStatic whether given dmh represents static method or not
+     * @param hasCallerParameter whether given dmh represents a method with an
+     *                         additional caller Class parameter
+     * @return transformed dmh to be used as a target in direct method accessors
+     */
+    static MethodHandle makeSpecializedTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
+        MethodHandle target = dmh.asFixedArity();
+
+        // number of formal arguments to the original method (not the adapter)
+        // If it is a non-static method, it has a leading `this` argument.
+        // Also do not count the caller class argument
+        int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
+        MethodType mtype = specializedMethodType(isStatic, hasCallerParameter, paramCount);
+        if (paramCount > SPECIALIZED_PARAM_COUNT) {
+            int spreadArgPos = isStatic ? 0 : 1;
+            target = target.asSpreader(spreadArgPos, Object[].class, paramCount);
+        }
+        if (isStatic) {
+            // add leading 'this' parameter to static method which is then ignored
+            target = MethodHandles.dropArguments(target, 0, Object.class);
+        }
+        return target.asType(mtype);
+    }
+
+    // specialize for number of formal arguments <= 3 to avoid spreader
+    static final int SPECIALIZED_PARAM_COUNT = 3;
+    static MethodType specializedMethodType(boolean isStatic, boolean hasCallerParameter, int paramCount) {
+        return switch (paramCount) {
+            case 0 -> hasCallerParameter ? methodType(Object.class, Object.class, Class.class)
+                                         : genericMethodType(1);
+            case 1 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Class.class)
+                                         : genericMethodType(2);
+            case 2 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Class.class)
+                                         : genericMethodType(3);
+            case 3 -> hasCallerParameter ? methodType(Object.class, Object.class, Object.class, Object.class, Object.class, Class.class)
+                                         : genericMethodType(4);
+            default -> hasCallerParameter ? methodType(Object.class, Object.class, Object[].class, Class.class)
+                                          : genericMethodType(1, true);
+        };
+    }
+
+    static MethodType specializedMethodTypeForConstructor(int paramCount) {
+        return switch (paramCount) {
+            case 0 ->  genericMethodType(0);
+            case 1 ->  genericMethodType(1);
+            case 2 ->  genericMethodType(2);
+            case 3 ->  genericMethodType(3);
+            default -> genericMethodType(0, true);
+        };
+    }
+
+    /**
+     * Transforms the given dmh into a target method handle with the method type
+     * {@code (Object, Object[])Object} or {@code (Object, Class, Object[])Object}
+     */
+    static MethodHandle makeTarget(MethodHandle dmh, boolean isStatic, boolean hasCallerParameter) {
+        MethodType mtype = hasCallerParameter
+                                ? methodType(Object.class, Object.class, Object[].class, Class.class)
+                                : genericMethodType(1, true);
+        // number of formal arguments
+        int paramCount = dmh.type().parameterCount() - (isStatic ? 0 : 1) - (hasCallerParameter ? 1 : 0);
+        int spreadArgPos = isStatic ? 0 : 1;
+        MethodHandle target = dmh.asFixedArity().asSpreader(spreadArgPos, Object[].class, paramCount);
+        if (isStatic) {
+            // add leading 'this' parameter to static method which is then ignored
+            target = MethodHandles.dropArguments(target, 0, Object.class);
+        }
+        return target.asType(mtype);
+    }
+
+    /**
+     * Ensures the given class is initialized.  If this is called from <clinit>,
+     * this method returns but defc's class initialization is not completed.
+     */
+    static void ensureClassInitialized(Class<?> defc) {
+        if (UNSAFE.shouldBeInitialized(defc)) {
+            UNSAFE.ensureClassInitialized(defc);
+        }
+    }
+
+    /*
+     * Returns true if NativeAccessor should be used.
+     */
+    private static boolean useNativeAccessor(Executable member) {
+        if (!VM.isJavaLangInvokeInited())
+            return true;
+
+        if (Modifier.isNative(member.getModifiers()))
+            return true;
+
+        if (ReflectionFactory.useNativeAccessorOnly())  // for testing only
+            return true;
+
+        // MethodHandle::withVarargs on a member with varargs modifier bit set
+        // verifies that the last parameter of the member must be an array type.
+        // The JVMS does not require the last parameter descriptor of the method descriptor
+        // is an array type if the ACC_VARARGS flag is set in the access_flags item.
+        // Hence the reflection implementation does not check the last parameter type
+        // if ACC_VARARGS flag is set.  Workaround this by invoking through
+        // the native accessor.
+        int paramCount = member.getParameterCount();
+        if (member.isVarArgs() &&
+                (paramCount == 0 || !(member.getParameterTypes()[paramCount-1].isArray()))) {
+            return true;
+        }
+        // A method handle cannot be created if its type has an arity >= 255
+        // as the method handle's invoke method consumes an extra argument
+        // of the method handle itself. Fall back to use the native implementation.
+        if (slotCount(member) >= MAX_JVM_ARITY) {
+            return true;
+        }
+        return false;
+    }
+
+    private static final int MAX_JVM_ARITY = 255;  // this is mandated by the JVM spec.
+    /*
+     * Return number of slots of the given member.
+     * - long/double args counts for two argument slots
+     * - A non-static method consumes an extra argument for the object on which
+     *   the method is called.
+     * - A constructor consumes an extra argument for the object which is being constructed.
+     */
+    private static int slotCount(Executable member) {
+        int slots = 0;
+        Class<?>[] ptypes = member.getParameterTypes();
+        for (Class<?> ptype : ptypes) {
+            if (ptype == double.class || ptype == long.class) {
+                slots++;
+            }
+        }
+        return ptypes.length + slots +
+                (Modifier.isStatic(member.getModifiers()) ? 0 : 1);
+    }
+
+    /*
+     * Delay initializing these static fields until java.lang.invoke is fully initialized.
+     */
+    static class LazyStaticHolder {
+        static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
+    }
+
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java
new file mode 100644
index 00000000000..10ec2f0e909
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleBooleanFieldAccessorImpl.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleBooleanFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(boolean.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, boolean.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(boolean.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, boolean.class));
+            }
+        }
+        return new MethodHandleBooleanFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleBooleanFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Boolean.valueOf(getBoolean(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        ensureObj(obj);
+        try {
+            if (isStatic()) {
+                return (boolean) getter.invokeExact();
+            } else {
+                return (boolean) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        throw newGetIntIllegalArgumentException();
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        throw newGetLongIllegalArgumentException();
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        throw newGetFloatIllegalArgumentException();
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        throw newGetDoubleIllegalArgumentException();
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        ensureObj(obj);
+        if (isReadOnly()) {
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Boolean b) {
+            setBoolean(obj, b.booleanValue());
+        } else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        ensureObj(obj);
+        if (isReadOnly()) {
+            throwFinalFieldIllegalAccessException(z);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(z);
+            } else {
+                setter.invokeExact(obj, z);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java
new file mode 100644
index 00000000000..7e88e080854
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleByteFieldAccessorImpl.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleByteFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(byte.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, byte.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(byte.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, byte.class));
+            }
+        }
+        return new MethodHandleByteFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleByteFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Byte.valueOf(getByte(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (byte) getter.invokeExact();
+            } else {
+                return (byte) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        return getByte(obj);
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        return getByte(obj);
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        return getByte(obj);
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        return getByte(obj);
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getByte(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        ensureObj(obj);
+        if (isReadOnly()) {
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setByte(obj, b.byteValue());
+        } else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        ensureObj(obj);
+        if (isReadOnly()) {
+            throwFinalFieldIllegalAccessException(b);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(b);
+            } else {
+                setter.invokeExact(obj, b);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java
new file mode 100644
index 00000000000..1e8b4e25525
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleCharacterFieldAccessorImpl.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleCharacterFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(char.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, char.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(char.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, char.class));
+            }
+        }
+        return new MethodHandleCharacterFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleCharacterFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Character.valueOf(getChar(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (char) getter.invokeExact();
+            } else {
+                return (char) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        return getChar(obj);
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        return getChar(obj);
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        return getChar(obj);
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getChar(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Character c) {
+            setChar(obj, c.charValue());
+        } else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(c);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(c);
+            } else {
+                setter.invokeExact(obj, c);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java
new file mode 100644
index 00000000000..2d370c331be
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleDoubleFieldAccessorImpl.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleDoubleFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(double.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, double.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(double.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, double.class));
+            }
+        }
+        return new MethodHandleDoubleFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleDoubleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Double.valueOf(getDouble(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        throw newGetIntIllegalArgumentException();
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        throw newGetLongIllegalArgumentException();
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        throw newGetFloatIllegalArgumentException();
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (double) getter.invokeExact();
+            } else {
+                return (double) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setDouble(obj, b.byteValue());
+        }
+        else if (value instanceof Short s) {
+            setDouble(obj, s.shortValue());
+        }
+        else if (value instanceof Character c) {
+            setDouble(obj, c.charValue());
+        }
+        else if (value instanceof Integer i) {
+            setDouble(obj, i.intValue());
+        }
+        else if (value instanceof Long l) {
+            setDouble(obj, l.longValue());
+        }
+        else if (value instanceof Float f) {
+            setDouble(obj, f.floatValue());
+        }
+        else if (value instanceof Double d) {
+            setDouble(obj, d.doubleValue());
+        }
+        else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setDouble(obj, f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(d);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(d);
+            } else {
+                setter.invokeExact(obj, d);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java
new file mode 100644
index 00000000000..ab59b61d1a2
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFieldAccessorImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Field;
+
+abstract class MethodHandleFieldAccessorImpl extends FieldAccessorImpl {
+    private static final int IS_READ_ONLY_BIT = 0x0001;
+    private static final int IS_STATIC_BIT = 0x0002;
+    private static final int NONZERO_BIT = 0x8000;
+
+    private final int fieldFlags;
+    protected final MethodHandle getter;
+    protected final MethodHandle setter;
+
+    protected MethodHandleFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field);
+        this.fieldFlags = (isReadOnly ? IS_READ_ONLY_BIT : 0) |
+                          (isStatic ? IS_STATIC_BIT : 0) |
+                          NONZERO_BIT;
+        this.getter = getter;
+        this.setter = setter;
+    }
+
+    protected final boolean isReadOnly() {
+        return (fieldFlags & IS_READ_ONLY_BIT) == IS_READ_ONLY_BIT;
+    }
+
+    protected final boolean isStatic() {
+        return (fieldFlags & IS_STATIC_BIT) == IS_STATIC_BIT;
+    }
+
+    protected final void ensureObj(Object o) {
+        if (!isStatic()) {
+            // for compatibility, check the receiver object first
+            // throw NullPointerException if o is null
+            if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
+                throwSetIllegalArgumentException(o);
+            }
+        }
+    }
+
+    /**
+     * IllegalArgumentException because Field::get on the specified object, which
+     * is not an instance of the class or interface declaring the underlying method
+     */
+    protected IllegalArgumentException newGetIllegalArgumentException(Class<?> type) {
+        return new IllegalArgumentException(getMessage(true, type.getName()));
+    }
+
+    /**
+     * IllegalArgumentException because Field::set on the specified object, which
+     * is not an instance of the class or interface declaring the underlying method
+     */
+    protected IllegalArgumentException newSetIllegalArgumentException(Class<?> type) {
+        return new IllegalArgumentException(getMessage(false, type.getName()));
+    }
+
+
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java
new file mode 100644
index 00000000000..ee1850f33a8
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleFloatFieldAccessorImpl.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleFloatFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(float.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, float.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(float.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, float.class));
+            }
+        }
+        return new MethodHandleFloatFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleFloatFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Float.valueOf(getFloat(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        throw newGetIntIllegalArgumentException();
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        throw newGetLongIllegalArgumentException();
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (float) getter.invokeExact();
+            } else {
+                return (float) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getFloat(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setFloat(obj, b.byteValue());
+        }
+        else if (value instanceof Short s) {
+            setFloat(obj, s.shortValue());
+        }
+        else if (value instanceof Character c) {
+            setFloat(obj, c.charValue());
+        }
+        else if (value instanceof Integer i) {
+            setFloat(obj, i.intValue());
+        }
+        else if (value instanceof Long l) {
+            setFloat(obj, l.longValue());
+        }
+        else if (value instanceof Float f) {
+            setFloat(obj, f.floatValue());
+        }
+        else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setFloat(obj, b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setFloat(obj, c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setFloat(obj, s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setFloat(obj, i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setFloat(obj, l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(f);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(f);
+            } else {
+                setter.invokeExact(obj, f);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java
new file mode 100644
index 00000000000..19a17cc1ca3
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleIntegerFieldAccessorImpl.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleIntegerFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(int.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, int.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(int.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, int.class));
+            }
+        }
+        return new MethodHandleIntegerFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleIntegerFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Integer.valueOf(getInt(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (int) getter.invokeExact();
+            } else {
+                return (int) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        return getInt(obj);
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        return getInt(obj);
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getInt(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setInt(obj, b.byteValue());
+        }
+        else if (value instanceof Short s) {
+            setInt(obj, s.shortValue());
+        }
+        else if (value instanceof Character c) {
+            setInt(obj, c.charValue());
+        }
+        else if (value instanceof Integer i) {
+            setInt(obj, i.intValue());
+        }
+        else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setInt(obj, b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setInt(obj, c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setInt(obj, s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(i);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(i);
+            } else {
+                setter.invokeExact(obj, i);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java
new file mode 100644
index 00000000000..42ef4c75c05
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleLongFieldAccessorImpl.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleLongFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(long.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, long.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(long.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, long.class));
+            }
+        }
+        return new MethodHandleLongFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleLongFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Long.valueOf(getLong(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        throw newGetIntIllegalArgumentException();
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (long) getter.invokeExact();
+            } else {
+                return (long) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        return getLong(obj);
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getLong(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setLong(obj, b.byteValue());
+        }
+        else if (value instanceof Short s) {
+            setLong(obj, s.shortValue());
+        }
+        else if (value instanceof Character c) {
+            setLong(obj, c.charValue());
+        }
+        else if (value instanceof Integer i) {
+            setLong(obj, i.intValue());
+        }
+        else if (value instanceof Long l) {
+            setLong(obj, l.longValue());
+        }
+        else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setLong(obj, b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setLong(obj, c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setLong(obj, s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setLong(obj, i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(l);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(l);
+            } else {
+                setter.invokeExact(obj, l);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java
new file mode 100644
index 00000000000..57da6081c84
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleObjectFieldAccessorImpl.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleObjectFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(Object.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, Object.class));
+            }
+        }
+        return new MethodHandleObjectFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleObjectFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    @Override
+    public Object get(Object obj) throws IllegalArgumentException {
+        try {
+            return isStatic() ? getter.invokeExact() : getter.invokeExact(obj);
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        throw newGetShortIllegalArgumentException();
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        throw newGetIntIllegalArgumentException();
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        throw newGetLongIllegalArgumentException();
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        throw newGetFloatIllegalArgumentException();
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        throw newGetDoubleIllegalArgumentException();
+    }
+
+    @Override
+    public void set(Object obj, Object value) throws IllegalAccessException {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(value);
+            } else {
+                setter.invokeExact(obj, value);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(s);
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java
new file mode 100644
index 00000000000..d9ac93d7276
--- /dev/null
+++ b/src/java.base/share/classes/jdk/internal/reflect/MethodHandleShortFieldAccessorImpl.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package jdk.internal.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+class MethodHandleShortFieldAccessorImpl extends MethodHandleFieldAccessorImpl {
+    static FieldAccessorImpl fieldAccessor(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly) {
+        boolean isStatic = Modifier.isStatic(field.getModifiers());
+        if (isStatic) {
+            getter = getter.asType(MethodType.methodType(short.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, short.class));
+            }
+        } else {
+            getter = getter.asType(MethodType.methodType(short.class, Object.class));
+            if (setter != null) {
+                setter = setter.asType(MethodType.methodType(void.class, Object.class, short.class));
+            }
+        }
+        return new MethodHandleShortFieldAccessorImpl(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    MethodHandleShortFieldAccessorImpl(Field field, MethodHandle getter, MethodHandle setter, boolean isReadOnly, boolean isStatic) {
+        super(field, getter, setter, isReadOnly, isStatic);
+    }
+
+    public Object get(Object obj) throws IllegalArgumentException {
+        return Short.valueOf(getShort(obj));
+    }
+
+    public boolean getBoolean(Object obj) throws IllegalArgumentException {
+        throw newGetBooleanIllegalArgumentException();
+    }
+
+    public byte getByte(Object obj) throws IllegalArgumentException {
+        throw newGetByteIllegalArgumentException();
+    }
+
+    public char getChar(Object obj) throws IllegalArgumentException {
+        throw newGetCharIllegalArgumentException();
+    }
+
+    public short getShort(Object obj) throws IllegalArgumentException {
+        try {
+            if (isStatic()) {
+                return (short) getter.invokeExact();
+            } else {
+                return (short) getter.invokeExact(obj);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newGetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public int getInt(Object obj) throws IllegalArgumentException {
+        return getShort(obj);
+    }
+
+    public long getLong(Object obj) throws IllegalArgumentException {
+        return getShort(obj);
+    }
+
+    public float getFloat(Object obj) throws IllegalArgumentException {
+        return getShort(obj);
+    }
+
+    public double getDouble(Object obj) throws IllegalArgumentException {
+        return getShort(obj);
+    }
+
+    public void set(Object obj, Object value)
+            throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(value);
+        }
+
+        if (value == null) {
+            throwSetIllegalArgumentException(value);
+        }
+
+        if (value instanceof Byte b) {
+            setShort(obj, b.byteValue());
+        }
+        else if (value instanceof Short s) {
+            setShort(obj, s.shortValue());
+        }
+        else {
+            throwSetIllegalArgumentException(value);
+        }
+    }
+
+    public void setBoolean(Object obj, boolean z)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(z);
+    }
+
+    public void setByte(Object obj, byte b)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        setShort(obj, b);
+    }
+
+    public void setChar(Object obj, char c)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(c);
+    }
+
+    public void setShort(Object obj, short s)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        if (isReadOnly()) {
+            ensureObj(obj);     // throw NPE if obj is null on instance field
+            throwFinalFieldIllegalAccessException(s);
+        }
+        try {
+            if (isStatic()) {
+                setter.invokeExact(s);
+            } else {
+                setter.invokeExact(obj, s);
+            }
+        } catch (IllegalArgumentException|NullPointerException e) {
+            throw e;
+        } catch (ClassCastException e) {
+            throw newSetIllegalArgumentException(obj.getClass());
+        } catch (Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public void setInt(Object obj, int i)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(i);
+    }
+
+    public void setLong(Object obj, long l)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(l);
+    }
+
+    public void setFloat(Object obj, float f)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(f);
+    }
+
+    public void setDouble(Object obj, double d)
+        throws IllegalArgumentException, IllegalAccessException
+    {
+        throwSetIllegalArgumentException(d);
+    }
+}
diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java
index 6cd7d7d0f01..c2a80c92296 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java
@@ -51,7 +51,7 @@ public Object newInstance(Object[] args)
                IllegalArgumentException,
                InvocationTargetException
     {
-        // We can't inflate a constructor belonging to a vm-anonymous class
+        // We can't inflate a constructor belonging to a hidden class
         // because that kind of class can't be referred to by name, hence can't
         // be found from the generated bytecode.
         if (++numInvocations > ReflectionFactory.inflationThreshold()
diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java
index 7f57035a0dd..756927be3ce 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java
@@ -49,7 +49,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
     public Object invoke(Object obj, Object[] args)
         throws IllegalArgumentException, InvocationTargetException
     {
-        // We can't inflate methods belonging to vm-anonymous classes because
+        // We can't inflate methods belonging to hidden classes because
         // that kind of class can't be referred to by name, hence can't be
         // found from the generated bytecode.
         if (++numInvocations > ReflectionFactory.inflationThreshold()
diff --git a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
index 17f0c7d95f2..5c0cf192984 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java
@@ -85,6 +85,15 @@ public class ReflectionFactory {
     private static boolean noInflation        = false;
     private static int     inflationThreshold = 15;
 
+    //
+    // New implementation uses direct invocation of method handles
+    private static final int METHOD_MH_ACCESSOR      = 0x1;
+    private static final int FIELD_MH_ACCESSOR       = 0x2;
+    private static final int ALL_MH_ACCESSORS        = METHOD_MH_ACCESSOR|FIELD_MH_ACCESSOR;
+
+    private static int     useDirectMethodHandle = ALL_MH_ACCESSORS;
+    private static boolean useNativeAccessorOnly = false;  // for testing only
+
     // true if deserialization constructor checking is disabled
     private static boolean disableSerialConstructorChecks = false;
 
@@ -137,24 +146,6 @@ public static ReflectionFactory getReflectionFactory() {
         return soleInstance;
     }
 
-    /**
-     * Returns an alternate reflective Method instance for the given method
-     * intended for reflection to invoke, if present.
-     *
-     * A trusted method can define an alternate implementation for a method `foo`
-     * by defining a method named "reflected$foo" that will be invoked
-     * reflectively.
-     */
-    private static Method findMethodForReflection(Method method) {
-        String altName = "reflected$" + method.getName();
-        try {
-           return method.getDeclaringClass()
-                        .getDeclaredMethod(altName, method.getParameterTypes());
-        } catch (NoSuchMethodException ex) {
-            return null;
-        }
-    }
-
     //--------------------------------------------------------------------------
     //
     // Routines used by java.lang.reflect
@@ -181,39 +172,48 @@ public FieldAccessor newFieldAccessor(Field field, boolean override) {
         }
         boolean isFinal = Modifier.isFinal(field.getModifiers());
         boolean isReadOnly = isFinal && (!override || langReflectAccess.isTrustedFinalField(field));
-        return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
+        if (useFieldHandleAccessor()) {
+            return MethodHandleAccessorFactory.newFieldAccessor(field, isReadOnly);
+        } else {
+            return UnsafeFieldAccessorFactory.newFieldAccessor(field, isReadOnly);
+        }
     }
 
-    public MethodAccessor newMethodAccessor(Method method) {
+    public MethodAccessor newMethodAccessor(Method method, boolean callerSensitive) {
         checkInitted();
 
-        if (Reflection.isCallerSensitive(method)) {
-            Method altMethod = findMethodForReflection(method);
-            if (altMethod != null) {
-                method = altMethod;
-            }
-        }
-
         // use the root Method that will not cache caller class
         Method root = langReflectAccess.getRoot(method);
         if (root != null) {
             method = root;
         }
 
-        if (noInflation && !method.getDeclaringClass().isHidden()) {
-            return new MethodAccessorGenerator().
-                generateMethod(method.getDeclaringClass(),
-                               method.getName(),
-                               method.getParameterTypes(),
-                               method.getReturnType(),
-                               method.getExceptionTypes(),
-                               method.getModifiers());
+        if (useMethodHandleAccessor()) {
+            return MethodHandleAccessorFactory.newMethodAccessor(method, callerSensitive);
         } else {
-            NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
-            return acc.getParent();
+            if (noInflation && !method.getDeclaringClass().isHidden()) {
+                return generateMethodAccessor(method);
+            } else {
+                NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
+                return acc.getParent();
+            }
         }
     }
 
+    /**
+     * Generate the MethodAccessor that invokes the given method with
+     * bytecode invocation.
+     */
+    static MethodAccessorImpl generateMethodAccessor(Method method) {
+        return (MethodAccessorImpl)new MethodAccessorGenerator()
+                .generateMethod(method.getDeclaringClass(),
+                                method.getName(),
+                                method.getParameterTypes(),
+                                method.getReturnType(),
+                                method.getExceptionTypes(),
+                                method.getModifiers());
+    }
+
     public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
         checkInitted();
 
@@ -232,23 +232,26 @@ public ConstructorAccessor newConstructorAccessor(Constructor<?> c) {
             c = root;
         }
 
-        // Bootstrapping issue: since we use Class.newInstance() in
-        // the ConstructorAccessor generation process, we have to
-        // break the cycle here.
-        if (Reflection.isSubclassOf(declaringClass,
-                                    ConstructorAccessorImpl.class)) {
-            return new BootstrapConstructorAccessorImpl(c);
-        }
-
-        if (noInflation && !c.getDeclaringClass().isHidden()) {
-            return new MethodAccessorGenerator().
-                generateConstructor(c.getDeclaringClass(),
-                                    c.getParameterTypes(),
-                                    c.getExceptionTypes(),
-                                    c.getModifiers());
+        if (useMethodHandleAccessor()) {
+            return MethodHandleAccessorFactory.newConstructorAccessor(c);
         } else {
-            NativeConstructorAccessorImpl acc = new NativeConstructorAccessorImpl(c);
-            return acc.getParent();
+            // Bootstrapping issue: since we use Class.newInstance() in
+            // the ConstructorAccessor generation process, we have to
+            // break the cycle here.
+            if (Reflection.isSubclassOf(declaringClass, ConstructorAccessorImpl.class)) {
+                return new BootstrapConstructorAccessorImpl(c);
+            }
+
+            if (noInflation && !c.getDeclaringClass().isHidden()) {
+                return new MethodAccessorGenerator().
+                        generateConstructor(c.getDeclaringClass(),
+                                            c.getParameterTypes(),
+                                            c.getExceptionTypes(),
+                                            c.getModifiers());
+            } else {
+                NativeConstructorAccessorImpl acc = new NativeConstructorAccessorImpl(c);
+                return acc.getParent();
+            }
         }
     }
 
@@ -623,6 +626,22 @@ static int inflationThreshold() {
         return inflationThreshold;
     }
 
+    static boolean noInflation() {
+        return noInflation;
+    }
+
+    static boolean useMethodHandleAccessor() {
+        return (useDirectMethodHandle & METHOD_MH_ACCESSOR) == METHOD_MH_ACCESSOR;
+    }
+
+    static boolean useFieldHandleAccessor() {
+        return (useDirectMethodHandle & FIELD_MH_ACCESSOR) == FIELD_MH_ACCESSOR;
+    }
+
+    static boolean useNativeAccessorOnly() {
+        return useNativeAccessorOnly;
+    }
+
     /** We have to defer full initialization of this class until after
         the static initializer is run since java.lang.reflect.Method's
         static initializer (more properly, that for
@@ -652,6 +671,20 @@ private static void checkInitted() {
                 throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
             }
         }
+        val = props.getProperty("jdk.reflect.useDirectMethodHandle");
+        if (val != null) {
+            if (val.equals("false")) {
+                useDirectMethodHandle = 0;
+            } else if (val.equals("methods")) {
+                useDirectMethodHandle = METHOD_MH_ACCESSOR;
+            } else if (val.equals("fields")) {
+                useDirectMethodHandle = FIELD_MH_ACCESSOR;
+            }
+        }
+        val = props.getProperty("jdk.reflect.useNativeAccessorOnly");
+        if (val != null && val.equals("true")) {
+            useNativeAccessorOnly = true;
+        }
 
         disableSerialConstructorChecks =
             "true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
diff --git a/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java
index 00e2bb129ef..9dd24f72915 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2021, 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
@@ -28,7 +28,6 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import jdk.internal.misc.Unsafe;
-import jdk.internal.vm.annotation.Stable;
 
 /** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The
     observation is that there are only nine types of fields from the
@@ -40,24 +39,17 @@
 abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl {
     static final Unsafe unsafe = Unsafe.getUnsafe();
 
-    @Stable
     protected final long    fieldOffset;
     protected final boolean isFinal;
 
     UnsafeFieldAccessorImpl(Field field) {
         super(field);
-        if (Modifier.isStatic(field.getModifiers()))
-            this.fieldOffset = unsafe.staticFieldOffset(field);
+        int mods = field.getModifiers();
+        this.isFinal = Modifier.isFinal(mods);
+        if (Modifier.isStatic(mods))
+            fieldOffset = unsafe.staticFieldOffset(field);
         else
-            this.fieldOffset = unsafe.objectFieldOffset(field);
-        this.isFinal = Modifier.isFinal(field.getModifiers());
-    }
-
-    protected void ensureObj(Object o) {
-        // NOTE: will throw NullPointerException, as specified, if o is null
-        if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) {
-            throwSetIllegalArgumentException(o);
-        }
+            fieldOffset = unsafe.objectFieldOffset(field);
     }
 
     protected boolean isFlattened() {
@@ -84,148 +76,4 @@ protected Object checkValue(Object value) {
         return value;
     }
 
-    private String getQualifiedFieldName() {
-      return field.getDeclaringClass().getName() + "." +field.getName();
-    }
-
-    protected IllegalArgumentException newGetIllegalArgumentException(String type) {
-        return new IllegalArgumentException(
-          "Attempt to get "+field.getType().getName()+" field \"" +
-          getQualifiedFieldName() + "\" with illegal data type conversion to "+type
-        );
-    }
-
-    protected void throwFinalFieldIllegalAccessException(String attemptedType,
-                                                         String attemptedValue)
-                                                         throws IllegalAccessException {
-        throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue));
-
-    }
-    protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", "");
-    }
-
-    protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("char", Character.toString(b));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("byte", Byte.toString(b));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("short", Short.toString(b));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("int", Integer.toString(i));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("long", Long.toString(i));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("float", Float.toString(f));
-    }
-
-    protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException {
-        throwFinalFieldIllegalAccessException("double", Double.toString(f));
-    }
-
-    protected IllegalArgumentException newGetBooleanIllegalArgumentException() {
-        return newGetIllegalArgumentException("boolean");
-    }
-
-    protected IllegalArgumentException newGetByteIllegalArgumentException() {
-        return newGetIllegalArgumentException("byte");
-    }
-
-    protected IllegalArgumentException newGetCharIllegalArgumentException() {
-        return newGetIllegalArgumentException("char");
-    }
-
-    protected IllegalArgumentException newGetShortIllegalArgumentException() {
-        return newGetIllegalArgumentException("short");
-    }
-
-    protected IllegalArgumentException newGetIntIllegalArgumentException() {
-        return newGetIllegalArgumentException("int");
-    }
-
-    protected IllegalArgumentException newGetLongIllegalArgumentException() {
-        return newGetIllegalArgumentException("long");
-    }
-
-    protected IllegalArgumentException newGetFloatIllegalArgumentException() {
-        return newGetIllegalArgumentException("float");
-    }
-
-    protected IllegalArgumentException newGetDoubleIllegalArgumentException() {
-        return newGetIllegalArgumentException("double");
-    }
-
-    protected String getSetMessage(String attemptedType, String attemptedValue) {
-        String err = "Can not set";
-        if (Modifier.isStatic(field.getModifiers()))
-            err += " static";
-        if (isFinal)
-            err += " final";
-        err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to ";
-        if (!attemptedValue.isEmpty()) {
-            err += "(" + attemptedType + ")" + attemptedValue;
-        } else {
-            if (!attemptedType.isEmpty())
-                err += attemptedType;
-            else
-                err += "null value";
-        }
-        return err;
-    }
-
-    protected void throwSetIllegalArgumentException(String attemptedType,
-                                                    String attemptedValue) {
-        throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue));
-    }
-
-    protected void throwSetIllegalArgumentException(Object o) {
-        throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", "");
-    }
-
-    protected void throwSetIllegalArgumentException(boolean b) {
-        throwSetIllegalArgumentException("boolean", Boolean.toString(b));
-    }
-
-    protected void throwSetIllegalArgumentException(byte b) {
-        throwSetIllegalArgumentException("byte", Byte.toString(b));
-    }
-
-    protected void throwSetIllegalArgumentException(char c) {
-        throwSetIllegalArgumentException("char", Character.toString(c));
-    }
-
-    protected void throwSetIllegalArgumentException(short s) {
-        throwSetIllegalArgumentException("short", Short.toString(s));
-    }
-
-    protected void throwSetIllegalArgumentException(int i) {
-        throwSetIllegalArgumentException("int", Integer.toString(i));
-    }
-
-    protected void throwSetIllegalArgumentException(long l) {
-        throwSetIllegalArgumentException("long", Long.toString(l));
-    }
-
-    protected void throwSetIllegalArgumentException(float f) {
-        throwSetIllegalArgumentException("float", Float.toString(f));
-    }
-
-    protected void throwSetIllegalArgumentException(double d) {
-        throwSetIllegalArgumentException("double", Double.toString(d));
-    }
-
 }
diff --git a/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java
index e75fffb1328..a52eae4edae 100644
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticFieldAccessorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2021, 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
@@ -26,13 +26,8 @@
 package jdk.internal.reflect;
 
 import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.security.AccessController;
 import java.util.Set;
 
-import jdk.internal.misc.Unsafe;
-import jdk.internal.vm.annotation.Stable;
-
 /** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static
     fields. The observation is that there are only nine types of
     fields from the standpoint of reflection code: the eight primitive
@@ -46,7 +41,6 @@ abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl {
                                           Set.of("base"));
     }
 
-    @Stable
     protected final Object base; // base
 
     UnsafeStaticFieldAccessorImpl(Field field) {
diff --git a/src/java.base/share/native/libjava/NativeAccessors.c b/src/java.base/share/native/libjava/NativeAccessors.c
index 53714124502..a0266f147b2 100644
--- a/src/java.base/share/native/libjava/NativeAccessors.c
+++ b/src/java.base/share/native/libjava/NativeAccessors.c
@@ -26,6 +26,8 @@
 #include "jvm.h"
 #include "jdk_internal_reflect_NativeConstructorAccessorImpl.h"
 #include "jdk_internal_reflect_NativeMethodAccessorImpl.h"
+#include "jdk_internal_reflect_DirectMethodHandleAccessor_NativeAccessor.h"
+#include "jdk_internal_reflect_DirectConstructorHandleAccessor_NativeAccessor.h"
 
 JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_NativeMethodAccessorImpl_invoke0
 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
@@ -38,3 +40,14 @@ JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_NativeConstructorAccessorImp
 {
     return JVM_NewInstanceFromConstructor(env, c, args);
 }
+
+JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_DirectMethodHandleAccessor_00024NativeAccessor_invoke0
+(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
+{
+    return JVM_InvokeMethod(env, m, obj, args);
+}
+JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_DirectConstructorHandleAccessor_00024NativeAccessor_newInstance0
+(JNIEnv *env, jclass unused, jobject c, jobjectArray args)
+{
+    return JVM_NewInstanceFromConstructor(env, c, args);
+}
diff --git a/src/java.logging/share/classes/java/util/logging/Logger.java b/src/java.logging/share/classes/java/util/logging/Logger.java
index 51ebfb58aea..0dc602c2c24 100644
--- a/src/java.logging/share/classes/java/util/logging/Logger.java
+++ b/src/java.logging/share/classes/java/util/logging/Logger.java
@@ -40,6 +40,7 @@
 import jdk.internal.access.JavaUtilResourceBundleAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.Reflection;
 import static jdk.internal.logger.DefaultLoggerFinder.isSystem;
 
@@ -714,6 +715,7 @@ public static Logger getLogger(String name) {
      *                          #getLogger(java.lang.String)}.
      * @return a suitable Logger for {@code callerClass}.
      */
+    @CallerSensitiveAdapter
     private static Logger getLogger(String name, Class<?> callerClass) {
         return demandLogger(name, null, callerClass);
     }
@@ -788,6 +790,7 @@ public static Logger getLogger(String name, String resourceBundleName) {
      *                          not {@code null}.
      * @return a suitable Logger for {@code callerClass}.
      */
+    @CallerSensitiveAdapter
     private static Logger getLogger(String name, String resourceBundleName,
                                     Class<?> callerClass) {
         Logger result = demandLogger(name, resourceBundleName, callerClass);
diff --git a/src/java.sql/share/classes/java/sql/DriverManager.java b/src/java.sql/share/classes/java/sql/DriverManager.java
index 9116e78efaf..f7733cfb62e 100644
--- a/src/java.sql/share/classes/java/sql/DriverManager.java
+++ b/src/java.sql/share/classes/java/sql/DriverManager.java
@@ -37,6 +37,7 @@
 import java.util.stream.Stream;
 
 import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
 import jdk.internal.reflect.Reflection;
 
 
@@ -647,6 +648,7 @@ public Void run() {
 
 
     //  Worker method called by the public getConnection() methods.
+    @CallerSensitiveAdapter
     private static Connection getConnection(
         String url, java.util.Properties info, Class<?> caller) throws SQLException {
         /*
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index 421eb3f0624..d1182feb154 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -38,6 +38,12 @@
 #
 #############################################################################
 
+vmTestbase/nsk/jvmti/AttachOnDemand/attach002a/TestDescription.java 8265795 generic-all
+vmTestbase/nsk/jvmti/AttachOnDemand/attach022/TestDescription.java 8265795 generic-all
+vmTestbase/nsk/jdi/ObjectReference/referringObjects/referringObjects002/referringObjects002.java 8265796  generic-all
+
+#############################################################################
+
 # :hotspot_compiler
 
 compiler/ciReplay/TestSAServer.java 8029528 generic-all
diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/ShowReflectionTargetTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/ShowReflectionTargetTest.java
index 53848e90009..a6e3fce1d24 100644
--- a/test/hotspot/jtreg/serviceability/dcmd/vm/ShowReflectionTargetTest.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/vm/ShowReflectionTargetTest.java
@@ -37,7 +37,7 @@
  * @summary Test that various diagnostic commands which can show core reflection
  *          invocation targets do so correctly (See: JDK-8203343).
  * @library /test/lib
- * @run testng/othervm -Dsun.reflect.noInflation=true ShowReflectionTargetTest
+ * @run testng/othervm -Dsun.reflect.noInflation=true -Djdk.reflect.useDirectMethodHandle=false ShowReflectionTargetTest
  * @author stuefe
  */
 
diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java
index 9b53d16925d..74ee07657d4 100644
--- a/test/jdk/com/sun/jdi/EATests.java
+++ b/test/jdk/com/sun/jdi/EATests.java
@@ -2198,7 +2198,9 @@ public void dontinline_callee_accessed_by_debugger() {
     @Override
     public void setUp() {
         super.setUp();
-        testMethodDepth = 8;
+        // the method depth in debuggee is 11 as it includes all hidden frames
+        // the expected method depth is 6 excluding 5 hidden frames
+        testMethodDepth = 11-5;
     }
 
     @Override
diff --git a/test/jdk/java/lang/StackWalker/DumpStackTest.java b/test/jdk/java/lang/StackWalker/DumpStackTest.java
index c0ff99f9f68..c196b1c4ad3 100644
--- a/test/jdk/java/lang/StackWalker/DumpStackTest.java
+++ b/test/jdk/java/lang/StackWalker/DumpStackTest.java
@@ -82,9 +82,7 @@ static void test() {
                 new CallFrame(DumpStackTest.class, "test"),
                 new CallFrame(DumpStackTest.class, "main"),
                 // if invoked from jtreg
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
-                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
+                new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"), // non-public class
                 new CallFrame(Method.class, "invoke"),
                 new CallFrame(Thread.class, "run"),
         };
@@ -138,9 +136,7 @@ static void consumeLambda() {
                 new CallFrame(DumpStackTest.class, "testLambda"),
                 new CallFrame(DumpStackTest.class, "main"),
                 // if invoked from jtreg
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
-                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
+                new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
                 new CallFrame(Method.class, "invoke"),
                 new CallFrame(Thread.class, "run")
         };
@@ -161,16 +157,12 @@ static void methodInvoke() {
         CallFrame[] callStack = new CallFrame[] {
                 new CallFrame(Thread.class, "getStackTrace"),
                 new CallFrame(DumpStackTest.class, "methodInvoke"),
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
-                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
+                new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
                 new CallFrame(Method.class, "invoke"),
                 new CallFrame(DumpStackTest.class, "testMethodInvoke"),
                 new CallFrame(DumpStackTest.class, "main"),
                 // if invoked from jtreg
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
-                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
+                new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
                 new CallFrame(Method.class, "invoke"),
                 new CallFrame(Thread.class, "run")
         };
@@ -196,9 +188,7 @@ static void methodHandle() {
                 new CallFrame(DumpStackTest.class, "testMethodHandle"),
                 new CallFrame(DumpStackTest.class, "main"),
                 // if invoked from jtreg
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
-                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
-                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
+                new CallFrame("jdk.internal.reflect.DirectMethodHandleAccessor", "invoke"),
                 new CallFrame(Method.class, "invoke"),
                 new CallFrame(Thread.class, "run")
         };
diff --git a/test/jdk/java/lang/StackWalker/NativeMethod.java b/test/jdk/java/lang/StackWalker/NativeMethod.java
index d6849649254..b1716a6b5ab 100644
--- a/test/jdk/java/lang/StackWalker/NativeMethod.java
+++ b/test/jdk/java/lang/StackWalker/NativeMethod.java
@@ -25,12 +25,11 @@
  * @test
  * @bug 8157892 8157977
  * @summary Verify file name, line number and bci of native methods
- * @run main NativeMethod
+ * @run main/othervm/native -Xcheck:jni NativeMethod
  */
 
 import java.lang.StackWalker.Option;
 import java.lang.StackWalker.StackFrame;
-import java.lang.reflect.Method;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -44,29 +43,36 @@ public static void main(String... args) throws Exception {
         this.walker = StackWalker.getInstance(Option.SHOW_REFLECT_FRAMES);
     }
 
-    void test() throws Exception {
-        // invoke via reflection that includes native methods
-        Method m = NativeMethod.class.getDeclaredMethod("walk");
-        m.invoke(this);
-    }
+    // this native method will invoke NativeMethod::walk method
+    public native void test();
 
-    void walk() {
+    public void walk() {
         List<StackFrame> nativeFrames = walker.walk(s ->
             s.filter(StackFrame::isNativeMethod)
              .collect(Collectors.toList())
         );
 
         assertTrue(nativeFrames.size() > 0, "native frame not found");
+        // find NativeMethod::test native frame
+        nativeFrames.stream().filter(f -> f.isNativeMethod()
+                                            && f.getClassName().equals("NativeMethod")
+                                            && f.getMethodName().equals("test"))
+                    .findFirst()
+                    .orElseThrow(() -> new RuntimeException("NativeMethod::test native method not found"));
+
         for (StackFrame f : nativeFrames) {
             assertTrue(f.getFileName() != null, "source file expected to be found");
             assertTrue(f.getLineNumber() < 0, "line number expected to be unavailable");
             assertTrue(f.getByteCodeIndex() < 0, "bci expected to be unavailable");
         }
-
     }
 
     private static void assertTrue(boolean value, String msg) {
         if (value != true)
             throw new AssertionError(msg);
     }
+
+    static {
+        System.loadLibrary("nativeMethod");
+    }
 }
diff --git a/test/jdk/java/lang/StackWalker/VerifyStackTrace.java b/test/jdk/java/lang/StackWalker/VerifyStackTrace.java
index 1cc6f349d8b..cf1c1ccb80a 100644
--- a/test/jdk/java/lang/StackWalker/VerifyStackTrace.java
+++ b/test/jdk/java/lang/StackWalker/VerifyStackTrace.java
@@ -100,15 +100,12 @@ static final class TestCase2 implements TestCase {
             "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +
             "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +
             "4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +
-            "5: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
-            "6: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
-            "7: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
-            "8: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
-            "9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
-            "10: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
-            "11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
-            "12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
-
+            "5: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:94)\n" +
+            "6: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
+            "7: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
+            "8: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
+            "9: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
+            "10: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
         @Override public StackWalker walker() { return walker;}
         @Override public String description() { return description;}
         @Override public String expected()    { return expected;}
@@ -130,22 +127,24 @@ static class TestCase3 implements TestCase {
         // then you can cut & paste the <-- actual --> stack printed in the
         // test output in here (don't forget the final \n):
         private final String expected =
-            "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" +
-            "2: VerifyStackTrace$$Lambda$1/0x00000007c0089430.run(Unknown Source)\n" +
-            "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" +
-            "4: java.base/java.lang.invoke.LambdaForm$DMH/0x00000007c008a830.invokeVirtual_LL_V(LambdaForm$DMH)\n" +
-            "5: java.base/java.lang.invoke.LambdaForm$MH/0x00000007c008a830.invoke_MT(LambdaForm$MH)\n" +
-            "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" +
-            "7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" +
-            "8: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
-            "9: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
-            "10: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
-            "11: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +
-            "12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" +
-            "13: java.base/java.security.AccessController.executePrivileged(AccessController.java:759)\n" +
-            "14: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +
-            "15: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" +
-            "16: VerifyStackTrace.main(VerifyStackTrace.java:188)\n";
+            "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:280)\n" +
+            "2: VerifyStackTrace$$Lambda$1/0x0000000801001848.run(Unknown Source)\n" +
+            "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:206)\n" +
+            "4: java.base/java.lang.invoke.DirectMethodHandle$Holder.invokeVirtual(DirectMethodHandle$Holder)\n" +
+            "5: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801004800.invoke_MT(LambdaForm$MH)\n" +
+            "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:219)\n" +
+            "7: VerifyStackTrace.invoke(VerifyStackTrace.java:259)\n" +
+            "8: java.base/java.lang.invoke.LambdaForm$DMH/0x0000000801002000.invokeStatic(LambdaForm$DMH)\n" +
+            "9: java.base/java.lang.invoke.LambdaForm$MH/0x0000000801003000.invoke(LambdaForm$MH)\n" +
+            "10: java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)\n" +
+            "11: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:211)\n" +
+            "12: java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:122)\n" +
+            "13: java.base/java.lang.reflect.Method.invoke(Method.java:573)\n" +
+            "14: VerifyStackTrace$1.run(VerifyStackTrace.java:292)\n" +
+            "15: java.base/java.security.AccessController.executePrivileged(AccessController.java:753)\n" +
+            "16: java.base/java.security.AccessController.doPrivileged(AccessController.java:312)\n" +
+            "17: VerifyStackTrace.test(VerifyStackTrace.java:301)\n" +
+            "18: VerifyStackTrace.main(VerifyStackTrace.java:254)\n";
 
         @Override public StackWalker walker() { return walker;}
         @Override public String description() { return description;}
diff --git a/test/jdk/java/lang/StackWalker/libnativeMethod.c b/test/jdk/java/lang/StackWalker/libnativeMethod.c
new file mode 100644
index 00000000000..aa2c3668fa6
--- /dev/null
+++ b/test/jdk/java/lang/StackWalker/libnativeMethod.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jni.h"
+
+static jclass test_class;
+static jmethodID mid;
+static jint current_jni_version = JNI_VERSION_10;
+
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv *env;
+    jclass cl;
+
+    (*vm)->GetEnv(vm, (void **) &env, current_jni_version);
+
+    cl = (*env)->FindClass(env, "NativeMethod");
+    test_class = (*env)->NewGlobalRef(env, cl);
+    mid = (*env)->GetMethodID(env, test_class, "walk", "()V");
+
+    return current_jni_version;
+}
+
+/*
+ * Class:     NativeMethod
+ * Method:    test
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_NativeMethod_test(JNIEnv *env, jobject obj) {
+    (*env)->CallVoidMethod(env, obj, mid);
+
+    if ((*env)->ExceptionCheck(env)) {
+        (*env)->ExceptionDescribe(env);
+        (*env)->FatalError(env, "Exception thrown");
+    }
+}
diff --git a/test/jdk/java/lang/invoke/CallerSensitiveMethodHandle.java b/test/jdk/java/lang/invoke/CallerSensitiveMethodHandle.java
new file mode 100644
index 00000000000..369c2c2bea1
--- /dev/null
+++ b/test/jdk/java/lang/invoke/CallerSensitiveMethodHandle.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, 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
+ * @run testng/othervm CallerSensitiveMethodHandle
+ * @summary Check Lookup findVirtual, findStatic and unreflect behavior with
+ *          caller sensitive methods with focus on AccessibleObject.setAccessible
+ */
+
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+
+import static java.lang.invoke.MethodType.*;
+import static org.testng.Assert.*;
+
+public class CallerSensitiveMethodHandle {
+    private static int field = 0;
+    @Test
+    public void privateField() throws Throwable {
+        Lookup l = MethodHandles.lookup();
+        Field f = CallerSensitiveMethodHandle.class.getDeclaredField("field");
+        MethodHandle mh = l.findVirtual(Field.class, "setInt", methodType(void.class, Object.class, int.class));
+        int newValue = 5;
+        mh.invokeExact(f, (Object) null, newValue);
+        assertTrue(field == newValue);
+    }
+
+    @Test
+    public void lookupItself() throws Throwable {
+        Lookup lookup = MethodHandles.lookup();
+        MethodHandle MH_lookup2 = lookup.findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
+        Lookup lookup2 = (Lookup) MH_lookup2.invokeExact();
+        System.out.println(lookup2 + " original lookup class " + lookup.lookupClass());
+        assertTrue(lookup2.lookupClass() == lookup.lookupClass());
+    }
+}
diff --git a/test/jdk/java/lang/invoke/MethodHandleInvokeUOE.java b/test/jdk/java/lang/invoke/MethodHandleInvokeUOE.java
new file mode 100644
index 00000000000..e728516fe03
--- /dev/null
+++ b/test/jdk/java/lang/invoke/MethodHandleInvokeUOE.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021, 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
+ * @summary Test MethodHandle::invokeExact and MethodHandle::invoke throws
+ *          UnsupportedOperationException when called via Method::invoke
+ * @run testng test.java.lang.invoke.MethodHandleInvokeUOE
+ */
+
+package test.java.lang.invoke;
+
+import org.testng.*;
+import org.testng.annotations.*;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static java.lang.invoke.MethodType.*;
+
+public class MethodHandleInvokeUOE {
+    @Test
+    public void testInvokeExact() throws Throwable {
+        Class<?> clz = MethodHandle.class;
+        String mname = "invokeExact";
+        Method m = clz.getDeclaredMethod(mname, Object[].class);
+        MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
+        try {
+            m.invoke(mh, new Object[1]);
+        } catch (InvocationTargetException e) {
+            if (!(e.getCause() instanceof UnsupportedOperationException)) {
+                throw new RuntimeException("expected UnsupportedOperationException but got: "
+                        + e.getCause().getClass().getName(), e);
+            }
+        }
+    }
+
+    @Test
+    public void testInvoke() throws Throwable {
+        Class<?> clz = MethodHandle.class;
+        String mname = "invoke";
+        Method m = clz.getDeclaredMethod(mname, Object[].class);
+        MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
+        try {
+            m.invoke(mh, new Object[1]);
+        } catch (InvocationTargetException e) {
+            if (!(e.getCause() instanceof UnsupportedOperationException)) {
+                throw new RuntimeException("expected UnsupportedOperationException but got: "
+                        + e.getCause().getClass().getName(), e);
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/lang/invoke/CallerSensitiveAccess.java b/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
similarity index 91%
rename from test/jdk/java/lang/invoke/CallerSensitiveAccess.java
rename to test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
index 2e904879a82..420cb37abde 100644
--- a/test/jdk/java/lang/invoke/CallerSensitiveAccess.java
+++ b/test/jdk/java/lang/invoke/callerSensitive/CallerSensitiveAccess.java
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 8196830 8235351
+ * @bug 8196830 8235351 8257874
  * @modules java.base/jdk.internal.reflect
  * @run testng/othervm CallerSensitiveAccess
  * @summary Check Lookup findVirtual, findStatic and unreflect behavior with
@@ -355,6 +355,40 @@ public void testLookupSubclass7() throws Throwable {
         mh.invoke(inaccessibleField(), true);  // should throw ClassCastException
     }
 
+    /**
+     * Field::getInt and Field::setInt are caller-sensitive methods.
+     * Test access to private field.
+     */
+    private static int privateField = 0;
+    @Test
+    public void testPrivateField() throws Throwable {
+        Field f = CallerSensitiveAccess.class.getDeclaredField("privateField");
+        // Field::setInt
+        MethodType mtype = MethodType.methodType(void.class, Object.class, int.class);
+        MethodHandle mh = MethodHandles.lookup().findVirtual(Field.class, "setInt", mtype);
+        mh.invokeExact(f, (Object)null, 5);
+        // Field::getInt
+        mh = MethodHandles.lookup().findVirtual(Field.class, "getInt", MethodType.methodType(int.class, Object.class));
+        int value = (int)mh.invokeExact(f, (Object)null);
+        assertTrue(value == 5);
+    }
+
+    private static class Inner {
+        private static boolean privateField = false;
+    }
+
+    @Test
+    public void testInnerPrivateField() throws Throwable {
+        Field f = Inner.class.getDeclaredField("privateField");
+        // Field::setInt
+        MethodType mtype = MethodType.methodType(void.class, Object.class, boolean.class);
+        MethodHandle mh = MethodHandles.lookup().findVirtual(Field.class, "setBoolean", mtype);
+        mh.invokeExact(f, (Object)null, true);
+        // Field::getInt
+        mh = MethodHandles.lookup().findVirtual(Field.class, "getBoolean", MethodType.methodType(boolean.class, Object.class));
+        boolean value = (boolean)mh.invokeExact(f, (Object)null);
+        assertTrue(value);
+    }
 
     // -- supporting methods --
 
diff --git a/test/jdk/java/lang/invoke/callerSensitive/Main.java b/test/jdk/java/lang/invoke/callerSensitive/Main.java
new file mode 100644
index 00000000000..437b26896b3
--- /dev/null
+++ b/test/jdk/java/lang/invoke/callerSensitive/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, 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 8013527
+ * @library src
+ * @modules java.base/jdk.internal.reflect
+ * @build java.base/java.util.CSM csm/*
+ * @run main/othervm -Djava.security.manager=allow csm/jdk.test.MethodInvokeTest
+ * @run main/othervm -Djava.security.manager=allow csm/jdk.test.MethodInvokeTest sm
+ * @summary Test proper caller class is bound to method handle for caller-sensitive method
+ */
+public class Main {
+}
+
diff --git a/test/jdk/java/lang/invoke/callerSensitive/csm/jdk/test/MethodInvokeTest.java b/test/jdk/java/lang/invoke/callerSensitive/csm/jdk/test/MethodInvokeTest.java
new file mode 100644
index 00000000000..732690044cd
--- /dev/null
+++ b/test/jdk/java/lang/invoke/callerSensitive/csm/jdk/test/MethodInvokeTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+package jdk.test;
+
+import java.lang.StackWalker.StackFrame;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.CSM;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.function.Supplier;
+
+/**
+ * This test invokes caller-sensitive methods via static reference,
+ * reflection, MethodHandle, lambda.  If there is no alternate implementation
+ * of a CSM with a trailing caller class parameter, when a CSM is invoked
+ * via method handle, an invoker class is injected as the caller class
+ * which is defined by the same defining class loader, in the same runtime
+ * package, and protection domain as the lookup class.
+ */
+public class MethodInvokeTest {
+    static final Policy DEFAULT_POLICY = Policy.getPolicy();
+    private static final String CALLER_METHOD = "caller";
+    private static final String CALLER_NO_ALT_METHOD = "callerNoAlternateImpl";
+
+    public static void main(String... args) throws Throwable {
+        boolean sm = args.length > 0 && args[0].equals("sm");
+        System.err.format("Test %s security manager.%n",
+                          sm ? "with" : "without");
+        if (sm) {
+            setupSecurityManager();
+        }
+
+        MethodInvokeTest test = new MethodInvokeTest();
+        // test static call to java.util.CSM::caller
+        test.staticMethodCall();
+        // test java.lang.reflect.Method call
+        test.reflectMethodCall();
+        // test java.lang.invoke.MethodHandle
+        test.invokeMethodHandle();
+        // test method ref
+        test.lambda();
+    }
+
+    static void setupSecurityManager() {
+        PermissionCollection perms = new Permissions();
+        perms.add(new RuntimePermission("getStackWalkerWithClassReference"));
+        Policy.setPolicy(new Policy() {
+            @Override
+            public boolean implies(ProtectionDomain domain, Permission p) {
+                return perms.implies(p) || DEFAULT_POLICY.implies(domain, p);
+            }
+        });
+        System.setSecurityManager(new SecurityManager());
+    }
+
+    void staticMethodCall() {
+        checkCaller(java.util.CSM.caller(), MethodInvokeTest.class, true);
+        checkCaller(java.util.CSM.callerNoAlternateImpl(), MethodInvokeTest.class, false);
+    }
+
+    void reflectMethodCall() throws Throwable {
+        // zero-arg caller method
+        checkCaller(Caller1.invoke(CSM.class.getMethod(CALLER_METHOD)), Caller1.class, true);
+        checkCaller(Caller2.invoke(CSM.class.getMethod(CALLER_METHOD)), Caller2.class, true);
+        // 4-arg caller method
+        checkCaller(Caller1.invoke(CSM.class.getMethod(CALLER_METHOD, Object.class, Object.class, Object.class, Object.class),
+                                   new Object[] { null, null, null, null}), Caller1.class, true);
+        checkCaller(Caller2.invoke(CSM.class.getMethod(CALLER_METHOD, Object.class, Object.class, Object.class, Object.class),
+                                   new Object[] { null, null, null, null}), Caller2.class, true);
+
+        // Reflection::getCallerClass will return the injected invoker class as the caller
+        checkInjectedInvoker(Caller1.invoke(CSM.class.getMethod(CALLER_NO_ALT_METHOD)), Caller1.class);
+        checkInjectedInvoker(Caller2.invoke(CSM.class.getMethod(CALLER_NO_ALT_METHOD)), Caller2.class);
+    }
+
+    void invokeMethodHandle() throws Throwable {
+        checkCaller(Caller1.invokeExact(CALLER_METHOD), Caller1.class, true);
+        checkCaller(Caller2.invokeExact(CALLER_METHOD), Caller2.class, true);
+
+        checkInjectedInvoker(Caller1.invokeExact(CALLER_NO_ALT_METHOD), Caller1.class);
+        checkInjectedInvoker(Caller2.invokeExact(CALLER_NO_ALT_METHOD), Caller2.class);
+    }
+
+    void lambda() {
+        CSM caller = LambdaTest.caller.get();
+        LambdaTest.checkLambdaProxyClass(caller);
+
+        caller = LambdaTest.caller();
+        LambdaTest.checkLambdaProxyClass(caller);
+    }
+
+    static class Caller1 {
+        static CSM invoke(Method csm) throws ReflectiveOperationException {
+            return (CSM)csm.invoke(null);
+        }
+        static CSM invoke(Method csm, Object[] args) throws ReflectiveOperationException {
+            return (CSM)csm.invoke(null, args);
+        }
+        static CSM invokeExact(String methodName) throws Throwable {
+            MethodHandle mh = MethodHandles.lookup().findStatic(java.util.CSM.class,
+                    methodName, MethodType.methodType(CSM.class));
+            return (CSM)mh.invokeExact();
+        }
+    }
+
+    static class Caller2 {
+        static CSM invoke(Method csm) throws ReflectiveOperationException {
+            return (CSM)csm.invoke(null);
+        }
+        static CSM invoke(Method csm, Object[] args) throws ReflectiveOperationException {
+            return (CSM)csm.invoke(null, args);
+        }
+        static CSM invokeExact(String methodName) throws Throwable {
+            MethodHandle mh = MethodHandles.lookup().findStatic(java.util.CSM.class,
+                    methodName, MethodType.methodType(CSM.class));
+            return (CSM)mh.invokeExact();
+        }
+    }
+
+    static class LambdaTest {
+        static Supplier<CSM> caller = java.util.CSM::caller;
+
+        static CSM caller() {
+            return caller.get();
+        }
+
+        /*
+         * The class calling the caller-sensitive method is the lambda proxy class
+         * generated for LambdaTest.
+         */
+        static void checkLambdaProxyClass(CSM csm) {
+            Class<?> caller = csm.caller;
+            assertTrue(caller.isHidden(), caller + " should be a hidden class");
+            assertEquals(caller.getModule(), LambdaTest.class.getModule());
+
+            int index = caller.getName().indexOf('/');
+            String cn = caller.getName().substring(0, index);
+            assertTrue(cn.startsWith(LambdaTest.class.getName() + "$$Lambda$"), caller + " should be a lambda proxy class");
+        }
+    }
+    static void checkCaller(CSM csm, Class<?> expected, boolean adapter) {
+        assertEquals(csm.caller, expected);
+        assertEquals(csm.adapter, adapter);
+        // verify no invoker class injected
+        for (StackFrame frame : csm.stackFrames) {
+            Class<?> c = frame.getDeclaringClass();
+            if (c == expected) break;
+
+            if (c.getName().startsWith(expected.getName() + "$$InjectedInvoker"))
+                throw new RuntimeException("should not have any invoker class injected");
+        }
+    }
+
+    /*
+     * The class calling the direct method handle of the caller-sensitive class
+     * is the InjectedInvoker class generated for the given caller.
+     */
+    static void checkInjectedInvoker(CSM csm, Class<?> expected) {
+        Class<?> invoker = csm.caller;
+        assertTrue(invoker.isHidden(), invoker + " should be a hidden class");
+        assertEquals(invoker.getModule(), expected.getModule());
+        assertEquals(csm.adapter, false);
+
+        int index = invoker.getName().indexOf('/');
+        String cn = invoker.getName().substring(0, index);
+        assertEquals(cn, expected.getName() + "$$InjectedInvoker");
+
+        // check the invoker class on the stack
+        for (StackFrame frame : csm.stackFrames) {
+            Class<?> c = frame.getDeclaringClass();
+            if (c.getName().startsWith(expected.getName() + "$$InjectedInvoker"))
+                break;
+
+            if (c == expected)
+                throw new RuntimeException("no invoker class found before the expected caller class");
+        }
+    }
+
+    static void assertTrue(boolean value, String msg) {
+        if (!value) {
+            throw new RuntimeException(msg);
+        }
+    }
+    static void assertEquals(Object o1, Object o2) {
+        if (!o1.equals(o2)) {
+            throw new RuntimeException(o1 + " != " + o2);
+        }
+    }
+}
diff --git a/test/jdk/java/lang/invoke/callerSensitive/csm/module-info.java b/test/jdk/java/lang/invoke/callerSensitive/csm/module-info.java
new file mode 100644
index 00000000000..5e7ed5b704f
--- /dev/null
+++ b/test/jdk/java/lang/invoke/callerSensitive/csm/module-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+module csm {
+    exports jdk.test;
+}
diff --git a/test/jdk/java/lang/invoke/callerSensitive/src/java.base/java/util/CSM.java b/test/jdk/java/lang/invoke/callerSensitive/src/java.base/java/util/CSM.java
new file mode 100644
index 00000000000..8f0dde04f6f
--- /dev/null
+++ b/test/jdk/java/lang/invoke/callerSensitive/src/java.base/java/util/CSM.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+package java.util;
+
+import java.lang.StackWalker.StackFrame;
+import java.util.stream.Stream;
+
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.CallerSensitiveAdapter;
+import jdk.internal.reflect.Reflection;
+
+import static java.lang.StackWalker.Option.*;
+
+public class CSM {
+    public final Class<?> caller;
+    public final List<StackFrame> stackFrames;
+    public final boolean adapter;
+    CSM(Class<?> caller, boolean adapter) {
+        this.caller = caller;
+        StackWalker sw = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
+        this.stackFrames = sw.walk(Stream::toList);
+        this.adapter = adapter;
+    }
+
+    /**
+     * Returns the caller of this caller-sensitive method returned by
+     * by Reflection::getCallerClass except if this method is called
+     * via method handle.
+     */
+    @CallerSensitive
+    public static CSM caller() {
+        return caller(Reflection.getCallerClass());
+    }
+
+    /**
+     * If caller() is invoked via method handle, this alternate method is
+     * called instead.  The caller class would be the lookup class.
+     */
+    @CallerSensitiveAdapter
+    private static CSM caller(Class<?> caller) {
+        return new CSM(caller, true);
+    }
+
+    /**
+     * Returns the caller of this caller-sensitive method returned by
+     * by Reflection::getCallerClass.
+     */
+    @CallerSensitive
+    public static CSM callerNoAlternateImpl() {
+        return new CSM(Reflection.getCallerClass(), false);
+    }
+
+    @CallerSensitive
+    public static CSM caller(Object o1, Object o2, Object o3, Object o4) {
+        return caller(o1, o2, o3, o4, Reflection.getCallerClass());
+    }
+
+    /**
+     * If caller() is invoked via method handle, this alternate method is
+     * called instead.  The caller class would be the lookup class.
+     */
+    @CallerSensitiveAdapter
+    private static CSM caller(Object o1, Object o2, Object o3, Object o4, Class<?> caller) {
+        return new CSM(caller, true);
+    }
+}
\ No newline at end of file
diff --git a/test/jdk/java/lang/invoke/lookup/ChainedLookupTest.java b/test/jdk/java/lang/invoke/lookup/ChainedLookupTest.java
new file mode 100644
index 00000000000..2aaa48af17b
--- /dev/null
+++ b/test/jdk/java/lang/invoke/lookup/ChainedLookupTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2021, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 8013527
+ * @run testng/othervm ChainedLookupTest
+ * @summary Test MethodHandles.lookup method to produce the Lookup object with
+ *          proper lookup class if invoked through reflection and method handle.
+ */
+
+import java.lang.invoke.*;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
+
+import org.testng.annotations.Test;
+
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+import static org.testng.Assert.*;
+
+public class ChainedLookupTest {
+    /**
+     * Direct call to MethodHandles::lookup
+     */
+    @Test
+    public void staticLookup() throws Throwable {
+        Lookup lookup =  lookup();
+        test(lookup, "Lookup produced via direct call to MethodHandles.lookup()");
+    }
+
+    /**
+     * Reflective Method::invoke on MethodHandles::lookup
+     */
+    @Test
+    public void methodInvoke() throws Throwable {
+        Method m =  MethodHandles.class.getMethod("lookup");
+        Lookup lookup = (Lookup) m.invoke(null);
+        test(lookup, "Lookup produced via reflective call");
+    }
+
+    /**
+     * Invoke Method::invoke on MethodHandles::lookup via MethodHandle
+     */
+    @Test
+    public void methodInvokeViaMethodHandle() throws Throwable {
+        Method m =  MethodHandles.class.getMethod("lookup");
+
+        MethodHandle mh = lookup().findVirtual(Method.class, "invoke",
+                                               methodType(Object.class, Object.class, Object[].class));
+        Lookup lookup = (Lookup)mh.invoke(m, (Object)null, (Object[])null);
+        test(lookup, "Lookup produced via Method::invoke via MethodHandle");
+    }
+
+    /**
+     * Invoke MethodHandle on MethodHandles::lookup via Method::invoke
+     */
+    // @Test
+    public void methodHandleViaReflection() throws Throwable {
+        MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
+        Lookup lookup = (Lookup) MH_lookup.invokeExact();
+
+        Method m =  MethodHandle.class.getMethod("invoke", Object[].class);
+
+        // should this throw UnsupportedOperationException per MethodHandle::invoke spec?
+        lookup = (Lookup)m.invoke(MH_lookup);
+        test(lookup, "Lookup produced via MethodHandle::invoke via Method::invoke");
+    }
+
+    /**
+     * MethodHandle::invokeExact on MethodHandles::lookup
+     */
+    @Test
+    public void methodHandle() throws Throwable {
+        MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
+        Lookup lookup = (Lookup) MH_lookup.invokeExact();
+        test(lookup, "Lookup produced via MethodHandle");
+    }
+
+    /**
+     * MethodHandles::unreflect the reflective Method representing MethodHandles::lookup
+     */
+    @Test
+    public void unreflect() throws Throwable {
+        MethodHandle MH_lookup = lookup().unreflect(MethodHandles.class.getMethod("lookup"));
+        Lookup lookup = (Lookup) MH_lookup.invokeExact();
+        test(lookup, "Lookup produced via unreflect MethodHandle");
+    }
+
+    /**
+     * Use the Lookup object returned from MethodHandle::invokeExact on MethodHandles::lookup
+     * to look up MethodHandle representing MethodHandles::lookup and then invoking it.
+     */
+    @Test
+    public void chainedMethodHandle() throws Throwable {
+        MethodHandle MH_lookup = lookup().findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
+        Lookup lookup = (Lookup) MH_lookup.invokeExact();
+        MethodHandle MH_lookup2 = lookup.unreflect(MethodHandles.class.getMethod("lookup"));
+        Lookup lookup2 = (Lookup) MH_lookup2.invokeExact();
+        test(lookup2, "Lookup produced via a chained call to MethodHandle");
+    }
+
+    void test(Lookup lookup, String msg) throws Throwable {
+        assertTrue(lookup.lookupClass() == ChainedLookupTest.class);
+        assertTrue(lookup.hasFullPrivilegeAccess());
+
+        MethodHandle mh = lookup.findStatic(lookup.lookupClass(), "say", methodType(void.class, String.class));
+        mh.invokeExact(msg);
+    }
+
+    private static void say(String msg) {
+        System.out.println(msg);
+    }
+}
+
diff --git a/test/jdk/java/lang/invoke/lookup/ReflectiveLookupTest.java b/test/jdk/java/lang/invoke/lookup/ReflectiveLookupTest.java
index 9d8eec4abd0..14178b7fa8f 100644
--- a/test/jdk/java/lang/invoke/lookup/ReflectiveLookupTest.java
+++ b/test/jdk/java/lang/invoke/lookup/ReflectiveLookupTest.java
@@ -52,9 +52,7 @@ public static void main(String... args) throws Throwable {
 
         Method lookupMethod =  MethodHandles.class.getMethod("lookup");
         System.out.println("reflection method: " + lookupMethod);
-        if (!lookupMethod.getName().equals("lookup")) {
-            throw new RuntimeException("Unexpected name: " + lookupMethod.getName());
-        }
+        assertEquals(lookupMethod.getName(), "lookup");
 
         // Get a full power Lookup reflectively.
         Lookup lookup2 = (Lookup) lookupMethod.invoke(null);
diff --git a/test/jdk/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java b/test/jdk/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java
index 3e5341baff3..3d633dc2082 100644
--- a/test/jdk/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java
+++ b/test/jdk/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java
@@ -56,7 +56,7 @@ public static void main(String... args) throws Throwable {
         try {
             MethodHandles.class.getMethod("lookup").invoke(null);
         } catch (InvocationTargetException e) {
-            if (!(e.getCause() instanceof IllegalArgumentException)) {
+            if (!(e.getCause() instanceof InternalError)) {
                 throw e.getCause();
             }
         }
diff --git a/test/jdk/java/lang/reflect/ChainedReflection.java b/test/jdk/java/lang/reflect/ChainedReflection.java
new file mode 100644
index 00000000000..212a70345cd
--- /dev/null
+++ b/test/jdk/java/lang/reflect/ChainedReflection.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2021, 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
+ * @run testng/othervm ChainedReflection
+ * @summary Test Method::invoke and Constructor::newInstance chained calls that
+ *          should wrap NPE in InvocationTargetException properly
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Optional;
+
+import org.testng.annotations.Test;
+
+public class ChainedReflection {
+    public ChainedReflection() {}
+
+    ChainedReflection(Void dummy) throws ReflectiveOperationException {
+        Method m = ChainedReflection.class.getMethod("throwNPE");
+        try {
+            m.invoke(null);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (t instanceof NullPointerException npe) {
+                throw npe;
+            } else {
+                throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
+            }
+        } catch (Throwable t) {
+            throw new RuntimeException("Test failed (Unexpected exception)", t);
+        }
+    }
+
+    public static void throwNPE() {
+        throw new NullPointerException();
+    }
+
+    /*
+     * Wrap a NullPointerException with a new NullPointerException
+     */
+    public static void npe() throws ReflectiveOperationException {
+        NullPointerException cause;
+        try {
+            Optional.of(null);
+            throw new RuntimeException("NPE not thrown");
+        } catch (NullPointerException e) {
+            cause = e;
+        }
+        Class<?> c = NullPointerException.class;
+        Constructor<?> ctor = c.getConstructor();
+        NullPointerException npe = (NullPointerException) ctor.newInstance();
+        npe.initCause(cause);
+        throw npe;
+    }
+
+    /**
+     * Tests the target method being invoked via core reflection that
+     * throws NullPointerException allocated via Constructor::newInstance.
+     * The newly allocated NPE thrown by the target method should be
+     * wrapped by InvocationTargetException.
+     */
+    @Test
+    public void methodCallNewInstance() throws ReflectiveOperationException {
+        Method m = ChainedReflection.class.getMethod("npe");
+        try {
+            m.invoke(null);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (!(t instanceof NullPointerException)) {
+                throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
+            }
+        } catch (Throwable t) {
+            throw new RuntimeException("Test failed (Unexpected exception)", t);
+        }
+    }
+
+    /**
+     * Tests a constructor C calling the method "throwNPE" that throws
+     * a new instance of NullPointerException.
+     * C::newInstance should wrap NullPointerException thrown by
+     * Method::invoke on "throwNPE"  by InvocationTargetException.
+     */
+    @Test
+    public void ctorCallMethodInvoke() throws ReflectiveOperationException {
+        Constructor<?> ctor = ChainedReflection.class.getDeclaredConstructor(Void.class);
+        try {
+            ctor.newInstance((Void)null);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (!(t instanceof NullPointerException)) {
+                throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)");
+            }
+        } catch (Throwable t) {
+            throw new RuntimeException("Test failed (Unexpected exception)", t);
+        }
+    }
+}
diff --git a/test/jdk/java/lang/reflect/Field/TestFieldReflectValueOf.java b/test/jdk/java/lang/reflect/Field/TestFieldReflectValueOf.java
index bc80c49f03a..e6ffa4a661b 100644
--- a/test/jdk/java/lang/reflect/Field/TestFieldReflectValueOf.java
+++ b/test/jdk/java/lang/reflect/Field/TestFieldReflectValueOf.java
@@ -87,25 +87,25 @@ public class TestFieldReflectValueOf {
     private volatile short shortVolatileField;
 
     public static void main(String[] args) {
-        testUnsafeStaticFieldAccessors();
-        testUnsafeQualifiedStaticFieldAccessors();
-        testUnsafeFieldAccessors();
-        testUnsafeQualifiedFieldAccessors();
+        testStaticFieldAccessors();
+        testStaticVolatileFieldAccessors();
+        testFieldAccessors();
+        testVolatileFieldAccessors();
     }
 
-    private static void testUnsafeStaticFieldAccessors() {
+    private static void testStaticFieldAccessors() {
         testFieldAccessors(true, false);
     }
 
-    private static void testUnsafeQualifiedStaticFieldAccessors() {
+    private static void testStaticVolatileFieldAccessors() {
         testFieldAccessors(true, true);
     }
 
-    private static void testUnsafeFieldAccessors() {
+    private static void testFieldAccessors() {
         testFieldAccessors(false, false);
     }
 
-    private static void testUnsafeQualifiedFieldAccessors() {
+    private static void testVolatileFieldAccessors() {
         testFieldAccessors(false, true);
     }
 
diff --git a/test/jdk/java/lang/reflect/Method/MethodArityLimit.java b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java
new file mode 100644
index 00000000000..536b3a5e317
--- /dev/null
+++ b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021, 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 8271820
+ * @run testng/othervm MethodArityLimit
+ * @summary Method exceeds the method handle arity limit (255).
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+public class MethodArityLimit {
+    @Test
+    public void testArityLimit() throws Throwable {
+        Method m = this.getClass().getMethod("f", long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, long.class,
+                long.class, long.class, long.class, long.class, long.class, int.class);
+
+        long resultViaMethod = (long) m.invoke(null, 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L,
+                8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 17L, 18L, 19L, 20L, 21L, 22L,
+                23L, 24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L,
+                37L, 38L, 39L, 40L, 41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 50L,
+                51L, 52L, 53L, 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 64L,
+                65L, 66L, 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 76L, 77L, 78L,
+                79L, 80L, 81L, 82L, 83L, 84L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L,
+                93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, 105L,
+                106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 116L, 117L,
+                118L, 119L, 120L, 121L, 122L, 123L, 124L, 125L, 126L, 127);
+
+        assertEquals(resultViaMethod, 127);
+
+        try {
+            MethodHandle mh = MethodHandles.lookup().unreflect(m);
+            fail("should fail in creating the method handle");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    public static long f(long a0, long a1, long a2, long a3, long a4, long a5,
+                         long a6, long a7, long a8, long a9, long a10, long a11, long a12,
+                         long a13, long a14, long a15, long a16, long a17, long a18, long a19,
+                         long a20, long a21, long a22, long a23, long a24, long a25, long a26,
+                         long a27, long a28, long a29, long a30, long a31, long a32, long a33,
+                         long a34, long a35, long a36, long a37, long a38, long a39, long a40,
+                         long a41, long a42, long a43, long a44, long a45, long a46, long a47,
+                         long a48, long a49, long a50, long a51, long a52, long a53, long a54,
+                         long a55, long a56, long a57, long a58, long a59, long a60, long a61,
+                         long a62, long a63, long a64, long a65, long a66, long a67, long a68,
+                         long a69, long a70, long a71, long a72, long a73, long a74, long a75,
+                         long a76, long a77, long a78, long a79, long a80, long a81, long a82,
+                         long a83, long a84, long a85, long a86, long a87, long a88, long a89,
+                         long a90, long a91, long a92, long a93, long a94, long a95, long a96,
+                         long a97, long a98, long a99, long a100, long a101, long a102, long a103,
+                         long a104, long a105, long a106, long a107, long a108, long a109,
+                         long a110, long a111, long a112, long a113, long a114, long a115,
+                         long a116, long a117, long a118, long a119, long a120, long a121,
+                         long a122, long a123, long a124, long a125, long a126, int a127) {
+        return a127;
+    }
+}
diff --git a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java
new file mode 100644
index 00000000000..3b702edc5f3
--- /dev/null
+++ b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2021, 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 8271820
+ * @modules java.base/jdk.internal.reflect
+ * @summary Test compliance of ConstructorAccessor, FieldAccessor, MethodAccessor implementations
+ * @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -Djdk.reflect.useDirectMethodHandle=true -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest
+ */
+
+/*
+ * @test
+ * @modules java.base/jdk.internal.reflect
+ * @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -Djdk.reflect.useDirectMethodHandle=false -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest
+ */
+
+
+import jdk.internal.reflect.ConstructorAccessor;
+import jdk.internal.reflect.FieldAccessor;
+import jdk.internal.reflect.MethodAccessor;
+import jdk.internal.reflect.Reflection;
+import jdk.internal.reflect.ReflectionFactory;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.IntUnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class MethodHandleAccessorsTest {
+
+    public static void public_static_V() {}
+
+    public static int public_static_I() { return 42; }
+
+    public void public_V() {}
+
+    public int public_I() { return 42; }
+
+    public static void public_static_I_V(int i) {}
+
+    public static int public_static_I_I(int i) { return i; }
+
+    public void public_I_V(int i) {}
+
+    public int public_I_I(int i) { return i; }
+
+    private static void private_static_V() {}
+
+    private static int private_static_I() { return 42; }
+
+    private void private_V() {}
+
+    private int private_I() { return 42; }
+
+    private static void private_static_I_V(int i) {}
+
+    private static int private_static_I_I(int i) { return i; }
+
+    private void private_I_V(int i) {}
+
+    private int private_I_I(int i) { return i; }
+
+    public static int varargs(int... values) {
+        int sum = 0;
+        for (int i : values) sum += i;
+        return sum;
+
+    }
+    public static int varargs_primitive(int first, int... rest) {
+        int sum = first;
+        if (rest != null) {
+            sum *= 100;
+            for (int i : rest) sum += i;
+        }
+        return sum;
+    }
+
+    public static String varargs_object(String first, String... rest) {
+        StringBuilder sb = new StringBuilder(first);
+        if (rest != null) {
+            sb.append(Stream.of(rest).collect(Collectors.joining(",", "[", "]")));
+        }
+        return sb.toString();
+    }
+
+    public static final class Public {
+        public static final int STATIC_FINAL = 1;
+        private final int i;
+        private final String s;
+
+        public Public() {
+            this.i = 0;
+            this.s = null;
+        }
+
+        public Public(int i) {
+            this.i = i;
+            this.s = null;
+        }
+
+        public Public(String s) {
+            this.i = 0;
+            this.s = s;
+        }
+
+        public Public(int first, int... rest) {
+            this(varargs_primitive(first, rest));
+        }
+
+        public Public(String first, String... rest) {
+            this(varargs_object(first, rest));
+        }
+
+        public Public(RuntimeException exc) {
+            throw exc;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Public other = (Public) o;
+            return i == other.i &&
+                   Objects.equals(s, other.s);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(i, s);
+        }
+
+        @Override
+        public String toString() {
+            return "Public{" +
+                   "i=" + i +
+                   ", s='" + s + '\'' +
+                   '}';
+        }
+    }
+
+    static final class Private {
+        private final int i;
+
+        private Private() {
+            this.i = 0;
+        }
+
+        private Private(int i) {
+            this.i = i;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Private other = (Private) o;
+            return i == other.i;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(i);
+        }
+
+        @Override
+        public String toString() {
+            return "Private{" +
+                   "i=" + i +
+                   '}';
+        }
+    }
+
+    static final class Thrower {
+        public Thrower(RuntimeException exc) {
+            throw exc;
+        }
+        public static void throws_exception(RuntimeException exc) {
+            throw exc;
+        }
+    }
+
+    public static abstract class Abstract {
+        public Abstract() {
+        }
+    }
+
+    /**
+     * Tests if MethodAccessor::invoke implementation returns the expected
+     * result or exceptions.
+     */
+    static void doTestAccessor(Method m, MethodAccessor ma, Object target, Object[] args,
+                               Object expectedReturn, Throwable... expectedExceptions) {
+        Object ret;
+        Throwable exc;
+        try {
+            ret = ma.invoke(target, args);
+            exc = null;
+        } catch (Throwable e) {
+            ret = null;
+            exc = e;
+        }
+        System.out.println("\n" + m + ", invoked with target: " + target + ", args: " + Arrays.toString(args));
+
+        chechResult(ret, expectedReturn, exc, expectedExceptions);
+    }
+
+    /**
+     * Tests if ConstructorAccessor::newInstance implementation returns the
+     * expected result or exceptions.
+     */
+    static void doTestAccessor(Constructor c, ConstructorAccessor ca, Object[] args,
+                               Object expectedReturn, Throwable... expectedExceptions) {
+        Object ret;
+        Throwable exc;
+        try {
+            ret = ca.newInstance(args);
+            exc = null;
+        } catch (Throwable e) {
+            ret = null;
+            exc = e;
+        }
+        System.out.println("\n" + c + ", invoked with args: " + Arrays.toString(args));
+        chechResult(ret, expectedReturn, exc, expectedExceptions);
+    }
+
+    /**
+     * Tests if FieldAccessor::get implementation returns the
+     * expected result or exceptions.
+     */
+    static void doTestAccessor(Field f, FieldAccessor fa, Object target,
+                               Object expectedValue, Throwable... expectedExceptions) {
+        Object ret;
+        Throwable exc;
+        try {
+            ret = fa.get(target);
+            exc = null;
+        } catch (Throwable e) {
+            ret = null;
+            exc = e;
+        }
+        System.out.println("\n" + f + ", invoked with target: " + target + ", value: " + ret);
+        chechResult(ret, expectedValue, exc, expectedExceptions);
+
+    }
+
+    /**
+     * Tests if FieldAccessor::set implementation returns the
+     * expected result or exceptions.
+     */
+    static void doTestAccessor(Field f, FieldAccessor fa, Object target, Object oldValue,
+                               Object newValue, Throwable... expectedExceptions) {
+        Object ret;
+        Throwable exc;
+            try {
+                fa.set(target, newValue);
+                exc = null;
+                ret = fa.get(target);
+            } catch (Throwable e) {
+                ret = null;
+                exc = e;
+            }
+            System.out.println("\n" + f + ", invoked with target: " + target + ", value: " + ret);
+            chechResult(ret, newValue, exc, expectedExceptions);
+    }
+
+    static void chechResult(Object ret, Object expectedReturn, Throwable exc, Throwable... expectedExceptions) {
+        if (exc != null) {
+            checkException(exc, expectedExceptions);
+        } else if (expectedExceptions.length > 0) {
+            fail(exc, expectedExceptions);
+        } else if (!Objects.equals(ret, expectedReturn)) {
+            throw new AssertionError("Expected return:\n " + expectedReturn + "\ngot:\n " + ret);
+        } else {
+            System.out.println("    Got expected return: " + ret);
+        }
+    }
+
+    static void checkException(Throwable exc, Throwable... expectedExceptions) {
+        boolean match = false;
+        for (Throwable expected : expectedExceptions) {
+            if (exceptionMatches(exc, expected)) {
+                match = true;
+                break;
+            }
+        }
+        if (match) {
+            System.out.println("    Got expected exception: " + exc);
+            if (exc.getCause() != null) {
+                System.out.println("                with cause: " + exc.getCause());
+            }
+        } else {
+            fail(exc, expectedExceptions);
+        }
+    }
+
+    static boolean exceptionMatches(Throwable exc, Throwable expected) {
+        return expected.getClass().isInstance(exc) &&
+                (Objects.equals(expected.getMessage(), exc.getMessage()) ||
+                        (exc.getMessage() != null && expected.getMessage() != null &&
+                         exc.getMessage().startsWith(expected.getMessage()))) &&
+                (expected.getCause() == null || exceptionMatches(exc.getCause(), expected.getCause()));
+    }
+
+    static void fail(Throwable thrownException, Throwable... expectedExceptions) {
+        String msg;
+        if (thrownException == null) {
+            msg = "No exception thrown but there were expected exceptions (see suppressed)";
+        } else if (expectedExceptions.length == 0) {
+            msg = "Exception thrown (see cause) but there were no expected exceptions";
+        } else {
+            msg = "Exception thrown (see cause) but expected exceptions were different (see suppressed)";
+        }
+        AssertionError error = new AssertionError(msg, thrownException);
+        Stream.of(expectedExceptions).forEach(error::addSuppressed);
+        throw error;
+    }
+
+    static void doTest(Method m, Object target, Object[] args, Object expectedReturn, Throwable... expectedException) {
+        MethodAccessor ma = ReflectionFactory.getReflectionFactory().newMethodAccessor(m, Reflection.isCallerSensitive(m));
+        try {
+            doTestAccessor(m, ma, target, args, expectedReturn, expectedException);
+        } catch (Throwable e) {
+            throw new RuntimeException(ma.getClass().getName() + " for method: " + m + " test failure", e);
+        }
+    }
+
+    static void doTest(Constructor c, Object[] args, Object expectedReturn, Throwable... expectedExceptions) {
+        ConstructorAccessor ca = ReflectionFactory.getReflectionFactory().newConstructorAccessor(c);
+        try {
+            doTestAccessor(c, ca, args, expectedReturn, expectedExceptions);
+        } catch (Throwable e) {
+            throw new RuntimeException(ca.getClass().getName() + " for constructor: " + c + " test failure", e);
+        }
+    }
+    static void doTest(Field f, Object target, Object expectedValue, Throwable... expectedExceptions) {
+        FieldAccessor fa = ReflectionFactory.getReflectionFactory().newFieldAccessor(f, false);
+        try {
+            doTestAccessor(f, fa, target, expectedValue, expectedExceptions);
+        } catch (Throwable e) {
+            throw new RuntimeException(fa.getClass().getName() + " for field: " + f + " test failure", e);
+        }
+    }
+    static void doTest(Field f, Object target, Object oldValue, Object newValue, Throwable... expectedExceptions) {
+        FieldAccessor fa = ReflectionFactory.getReflectionFactory().newFieldAccessor(f, true);
+        try {
+            doTestAccessor(f, fa, target, oldValue, newValue, expectedExceptions);
+        } catch (Throwable e) {
+            throw new RuntimeException(fa.getClass().getName() + " for field: " + f + " test failure", e);
+        }
+    }
+
+    private static final Throwable[] noException = new Throwable[0];
+    private static final Throwable[] mismatched_argument_type = new Throwable[] {
+            new IllegalArgumentException("argument type mismatch")
+    };
+    private static final Throwable[] mismatched_target_type = new Throwable[] {
+            new IllegalArgumentException("argument type mismatch"),
+            new IllegalArgumentException("object is not an instance of declaring class"),
+    };
+    private static final Throwable[] cannot_get_final_field = new Throwable[] {
+            new IllegalArgumentException("Can not get final")
+    };
+    private static final Throwable[] cannot_set_final_field = new Throwable[] {
+            new IllegalArgumentException("Can not set final")
+    };
+    private static final Throwable[] wrong_argument_count_no_details = new Throwable[] {
+            new IllegalArgumentException("wrong number of arguments")
+    };
+    private static final Throwable[] wrong_argument_count = new Throwable[] {
+            new IllegalArgumentException("wrong number of arguments"),
+            new IllegalArgumentException("array is not of length 1")
+    };
+    private static final Throwable[] null_argument = new Throwable[] {
+            new IllegalArgumentException("wrong number of arguments"),
+            new IllegalArgumentException("null array reference")
+    };
+    private static final Throwable[] null_argument_value = new Throwable[] {
+            new IllegalArgumentException()
+    };
+    private static final Throwable[] null_argument_value_npe = new Throwable[] {
+            new IllegalArgumentException("java.lang.NullPointerException"),
+            new NullPointerException()
+    };
+    private static final Throwable[] null_target = new Throwable[] {
+            new NullPointerException()
+    };
+    private static final Throwable[] wrapped_npe_no_msg = new Throwable[]{
+            new InvocationTargetException(new NullPointerException())
+    };
+    private static final Throwable[] wrapped_npe = new Throwable[]{
+            new InvocationTargetException(new NullPointerException("NPE"))
+    };
+    private static final Throwable[] wrapped_cce = new Throwable[]{
+            new InvocationTargetException(new ClassCastException("CCE"))
+    };
+    private static final Throwable[] wrapped_iae = new Throwable[]{
+            new InvocationTargetException(new IllegalArgumentException("IAE"))
+    };
+
+
+    @DataProvider(name = "testNoArgMethods")
+    private Object[][] testNoArgMethods() {
+        MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest();
+        Object[] emptyArgs = new Object[]{};
+        return new Object[][] {
+             new Object[] {"public_static_V",   null, emptyArgs, null, noException},
+             new Object[] {"public_static_V",   null, null, null, noException},
+             new Object[] {"public_static_I",   null, emptyArgs, 42, noException},
+             new Object[] {"public_static_I",   null, null, 42, noException},
+             new Object[] {"public_V",          inst, emptyArgs, null, noException},
+             new Object[] {"public_V",          inst, null, null, noException},
+             new Object[] {"public_I",          inst, emptyArgs, 42, noException},
+             new Object[] {"public_I",          inst, null, 42, noException},
+             new Object[] {"private_static_V",  null, emptyArgs, null, noException},
+             new Object[] {"private_static_I",  null, emptyArgs, 42, noException},
+             new Object[] {"private_V",         inst, emptyArgs, null, noException},
+             new Object[] {"private_I",         inst, emptyArgs, 42, noException},
+             new Object[] {"public_V",          null, null, null, null_target},
+        };
+    }
+
+    @DataProvider(name = "testOneArgMethods")
+    private Object[][] testOneArgMethods() {
+        MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest();
+        Object wrongInst = new Object();
+        boolean newImpl = Boolean.getBoolean("jdk.reflect.useDirectMethodHandle");
+        return new Object[][]{
+            new Object[] {"public_static_I_V",  int.class, null, new Object[]{12}, null, noException},
+            new Object[] {"public_static_I_I",  int.class, null, new Object[]{12}, 12, noException},
+            new Object[] {"public_I_V",         int.class, inst, new Object[]{12}, null, noException},
+            new Object[] {"public_I_I",         int.class, inst, new Object[]{12}, 12, noException},
+            new Object[] {"private_static_I_V", int.class, null, new Object[]{12}, null, noException},
+            new Object[] {"private_static_I_I", int.class, null, new Object[]{12}, 12, noException},
+            new Object[] {"private_I_V",        int.class, inst, new Object[]{12}, null, noException},
+            new Object[] {"private_I_I",        int.class, inst, new Object[]{12}, 12, noException},
+
+            new Object[] {"public_static_I_I", int.class, null, new Object[]{"a"}, null, mismatched_argument_type},
+            new Object[] {"public_I_I",        int.class, inst, new Object[]{"a"}, null, mismatched_argument_type},
+            new Object[] {"public_static_I_I", int.class, null, new Object[]{12, 13}, null,
+                          newImpl ? wrong_argument_count_no_details : wrong_argument_count},
+            new Object[] {"public_I_I",        int.class, inst, new Object[]{12, 13}, null,
+                          newImpl ? wrong_argument_count_no_details : wrong_argument_count},
+            new Object[] {"public_I_I",        int.class, wrongInst, new Object[]{12}, 12, mismatched_target_type},
+            new Object[] {"public_I_I",        int.class, null, new Object[]{12}, 12, null_target},
+
+            new Object[] {"public_static_I_V", int.class, null, null, null,
+                          newImpl ? wrong_argument_count_no_details : null_argument},
+            new Object[] {"public_static_I_V", int.class, null, new Object[]{null}, null,
+                          newImpl ? null_argument_value_npe : null_argument_value},
+            new Object[] {"public_I_I",        int.class, inst, null, null, null_argument},
+
+            new Object[] {"public_I_I", int.class, inst, new Object[]{null}, null,
+                          newImpl ? null_argument_value_npe : null_argument_value},
+        };
+    }
+
+    @DataProvider(name = "testMethodsWithVarargs")
+    private Object[][] testMethodsWithVarargs() {
+        Class<?>[] paramTypes = new Class<?>[] { int[].class };
+        Class<?>[] I_paramTypes = new Class<?>[] { int.class, int[].class };
+        Class<?>[] L_paramTypes = new Class<?>[] { String.class, String[].class };
+        return new Object[][]{
+            new Object[] {"varargs", paramTypes, null, new Object[]{new int[]{1, 2, 3}}, 6, noException},
+            new Object[] {"varargs", paramTypes, null, new Object[]{new int[]{}}, 0, noException},
+            new Object[] {"varargs", paramTypes, null, new Object[]{null}, 0, wrapped_npe_no_msg},
+            new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, new int[]{2, 3}}, 105, noException},
+            new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, new int[]{}}, 100, noException},
+            new Object[] {"varargs_primitive", I_paramTypes, null, new Object[]{1, null}, 1, noException},
+            new Object[] {"varargs_object", L_paramTypes,    null, new Object[]{"a", new String[]{"b", "c"}}, "a[b,c]", noException},
+            new Object[] {"varargs_object", L_paramTypes,    null, new Object[]{"a", new String[]{}}, "a[]", noException},
+            new Object[] {"varargs_object", L_paramTypes,    null, new Object[]{"a", null}, "a", noException},
+        };
+    }
+
+    @Test(dataProvider = "testNoArgMethods")
+    public void testNoArgMethod(String methodname, Object target, Object[] args,
+                                Object expectedReturn, Throwable[] expectedExpections) throws Exception {
+        doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname), target, args, expectedReturn, expectedExpections);
+    }
+
+    @Test(dataProvider = "testOneArgMethods")
+    public void testOneArgMethod(String methodname, Class<?> paramType, Object target, Object[] args,
+                                 Object expectedReturn, Throwable[] expectedExpections) throws Exception {
+        doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramType), target, args, expectedReturn, expectedExpections);
+    }
+
+    @Test(dataProvider = "testMethodsWithVarargs")
+    public void testMethodsWithVarargs(String methodname, Class<?>[] paramTypes, Object target, Object[] args,
+                                       Object expectedReturn, Throwable[] expectedExpections) throws Exception {
+        doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections);
+    }
+
+    @DataProvider(name = "testConstructors")
+    private Object[][] testConstructors() {
+        return new Object[][]{
+                new Object[]{null, new Object[]{}, new Public(), noException},
+                new Object[]{null, null, new Public(), noException},
+                new Object[]{new Class<?>[]{int.class}, new Object[]{12}, new Public(12), noException},
+                new Object[]{new Class<?>[]{String.class}, new Object[]{"a"}, new Public("a"), noException},
+
+
+                new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, new int[]{2, 3}}, new Public(105), noException},
+                new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, new int[]{}}, new Public(100), noException},
+                new Object[]{new Class<?>[]{int.class, int[].class}, new Object[]{1, null}, new Public(1), noException},
+
+                new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", new String[]{"b", "c"}}, new Public("a[b,c]"), noException},
+                new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", new String[]{}}, new Public("a[]"), noException},
+                new Object[]{new Class<?>[]{String.class, String[].class}, new Object[]{"a", null}, new Public("a"), noException},
+
+                // test ConstructorAccessor exceptions thrown
+                new Object[]{new Class<?>[]{int.class}, new Object[]{"a"}, null, mismatched_argument_type},
+                new Object[]{new Class<?>[]{int.class}, new Object[]{12, 13}, null, wrong_argument_count},
+                new Object[]{new Class<?>[]{int.class}, null, null, null_argument},
+                new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new NullPointerException("NPE")}, null, wrapped_npe},
+                new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new IllegalArgumentException("IAE")}, null, wrapped_iae},
+                new Object[]{new Class<?>[]{RuntimeException.class}, new Object[]{new ClassCastException("CCE")}, null, wrapped_cce},
+        };
+    }
+
+    @Test(dataProvider = "testConstructors")
+    public void testPublicConstructors(Class<?>[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception {
+        doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections);
+    }
+
+    @Test
+    public void testOtherConstructors() throws Exception {
+        doTest(Private.class.getDeclaredConstructor(), new Object[]{}, new Private());
+        doTest(Private.class.getDeclaredConstructor(), null, new Private());
+        doTest(Private.class.getDeclaredConstructor(int.class), new Object[]{12}, new Private(12));
+
+        doTest(Abstract.class.getDeclaredConstructor(), null, null, new InstantiationException());
+    }
+
+    @DataProvider(name = "throwException")
+    private Object[][] throwException() {
+        return new Object[][]{
+                new Object[] {new NullPointerException("NPE"), wrapped_npe},
+                new Object[] {new IllegalArgumentException("IAE"), wrapped_iae},
+                new Object[] {new ClassCastException("CCE"), wrapped_cce},
+        };
+    }
+
+    /*
+     * Test Method::invoke and Constructor::newInstance to wrap NPE/CCE/IAE
+     * thrown by the member
+     */
+    @Test(dataProvider = "throwException")
+    public void testInvocationTargetException(Throwable ex, Throwable[] expectedExpections) throws Exception {
+        Object[] args = new Object[] { ex };
+        // test static method
+        doTest(Thrower.class.getDeclaredMethod("throws_exception", RuntimeException.class), null, args, null, expectedExpections);
+        // test constructor
+        doTest(Thrower.class.getDeclaredConstructor(RuntimeException.class), args, null, expectedExpections);
+    }
+
+    @Test
+    public void testLambdaProxyClass() throws Exception {
+        // test MethodAccessor on methods declared by hidden classes
+        IntUnaryOperator intUnaryOp = i -> i;
+        Method applyAsIntMethod = intUnaryOp.getClass().getDeclaredMethod("applyAsInt", int.class);
+        doTest(applyAsIntMethod, intUnaryOp, new Object[]{12}, 12);
+    }
+
+    @DataProvider(name = "readAccess")
+    private Object[][] readAccess() {
+        boolean newImpl = Boolean.getBoolean("jdk.reflect.useDirectMethodHandle");
+        return new Object[][]{
+                new Object[]{"i", new Public(100), 100, noException},
+                new Object[]{"s", new Public("test"), "test", noException},
+                new Object[]{"s", new Object(), "test",
+                             newImpl ? cannot_get_final_field : cannot_set_final_field},
+                new Object[]{"s", null, "test", null_target},
+        };
+    }
+    @DataProvider(name = "writeAccess")
+    private Object[][] writeAccess() {
+        return new Object[][]{
+                new Object[]{"i", new Public(100), 100, 200, noException},
+                new Object[]{"s", new Public("test"), "test", "newValue", noException},
+                // ## no exception thrown
+                // new Object[]{"i", new Public(100), 100, new Object(), cannot_set_final_field},
+                new Object[]{"s", new Object(), "test", "dummy", cannot_set_final_field},
+                new Object[]{"s", null, "test", "dummy", null_target},
+        };
+    }
+
+    @Test(dataProvider = "readAccess")
+    public void testFieldReadAccess(String name, Object target, Object expectedValue, Throwable[] expectedExpections) throws Exception {
+        Field f = Public.class.getDeclaredField(name);
+        f.setAccessible(true);
+        doTest(f, target, expectedValue, expectedExpections);
+    }
+
+    @Test(dataProvider = "writeAccess")
+    public void testFieldWriteAccess(String name, Object target, Object oldValue, Object newValue, Throwable[] expectedExpections) throws Exception {
+        Field f = Public.class.getDeclaredField(name);
+        f.setAccessible(true);
+        doTest(f, target, oldValue, newValue, expectedExpections);
+    }
+
+    // test static final field with read-only access
+    @Test
+    public void testStaticFinalFields() throws Exception {
+        Field f = Public.class.getDeclaredField("STATIC_FINAL");
+        doTest(f, new Public(), 1, noException);
+
+        try {
+            f.setInt(null, 100);
+        } catch (IllegalAccessException e) { }
+    }
+}
diff --git a/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java
new file mode 100644
index 00000000000..27710b5909f
--- /dev/null
+++ b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2021, 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 8271820
+ * @library /test/lib/
+ * @modules jdk.compiler
+ * @build CustomLoaderTest jdk.test.lib.compiler.CompilerUtils
+ * @run testng/othervm CustomLoaderTest
+ * @run testng/othervm -Dsun.reflect.noInflation=true CustomLoaderTest
+ *
+ * @summary Test method whose parameter types and return type are not visible to the caller.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.test.lib.compiler.CompilerUtils;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class CustomLoaderTest {
+    private static final Path CLASSES = Paths.get("classes");
+
+    @BeforeTest
+    public void setup() throws IOException {
+        String src = System.getProperty("test.src", ".");
+        String classpath = System.getProperty("test.classes", ".");
+        boolean rc = CompilerUtils.compile(Paths.get(src, "ReflectTest.java"), CLASSES, "-cp", classpath);
+        if (!rc) {
+            throw new RuntimeException("fail compilation");
+        }
+        try {
+            Class<?> p = Class.forName("ReflectTest$P");
+            fail("should not be visible to this loader");
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test() throws Exception {
+        TestLoader loader1 = new TestLoader();
+        TestLoader loader2 = new TestLoader();
+        Method m1 = loader1.findMethod();
+        Method m2 = loader2.findMethod();
+
+        assertTrue(m1.getDeclaringClass() != m2.getDeclaringClass());
+
+        assertTrue(m1.getDeclaringClass() == loader1.c);
+        assertTrue(m2.getDeclaringClass() == loader2.c);
+
+        Object o1 = m1.invoke(loader1.c.newInstance(), loader1.p.newInstance(), loader1.q.newInstance());
+        Object o2 = m2.invoke(loader2.c.newInstance(), loader2.p.newInstance(), loader2.q.newInstance());
+
+        assertTrue(o1.getClass() != o2.getClass());
+        assertTrue(o1.getClass() == loader1.r);
+        assertTrue(o2.getClass() == loader2.r);
+    }
+
+    static class TestLoader extends URLClassLoader {
+        static URL[] toURLs() {
+            try {
+                return new URL[]{ CLASSES.toUri().toURL() };
+            } catch (MalformedURLException e) {
+                throw new Error(e);
+            }
+        }
+        static AtomicInteger counter = new AtomicInteger(0);
+
+        final Class<?> c;
+        final Class<?> p;
+        final Class<?> q;
+        final Class<?> r;
+        TestLoader() throws ClassNotFoundException {
+            super("testloader-" + counter.getAndIncrement(), toURLs(), ClassLoader.getPlatformClassLoader());
+            this.c = Class.forName("ReflectTest", true, this);
+            this.p = Class.forName("ReflectTest$P", true, this);
+            this.q = Class.forName("ReflectTest$Q", true, this);
+            this.r = Class.forName("ReflectTest$R", true, this);
+        }
+
+        Method findMethod() throws ReflectiveOperationException {
+            return c.getMethod("m", p, q);
+        }
+    }
+}
diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectTest.java
new file mode 100644
index 00000000000..91e882112cc
--- /dev/null
+++ b/test/jdk/java/lang/reflect/callerCache/ReflectTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+public class ReflectTest {
+     public static class P {
+     }
+     public static class Q {
+     }
+     public static class R {
+     }
+
+     public R m(P p, Q q) {
+         return new R();
+     }
+}
diff --git a/test/jdk/java/lang/reflect/classInitialization/ExceptionInClassInitialization.java b/test/jdk/java/lang/reflect/classInitialization/ExceptionInClassInitialization.java
new file mode 100644
index 00000000000..64f3fa17f63
--- /dev/null
+++ b/test/jdk/java/lang/reflect/classInitialization/ExceptionInClassInitialization.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2021, 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
+ * @build Initializer Test ExceptionInClassInitialization
+ * @run main ExceptionInClassInitialization
+ * @summary ensure InvocationTargetException thrown due to the initialization of
+ *          the declaring class wrapping with the proper cause
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+public class ExceptionInClassInitialization {
+    public static void main(String... argv) throws ReflectiveOperationException {
+        Class<?> c = Class.forName("Initializer");
+        testExecMethod(c);
+        testFieldAccess(c);
+    }
+
+    static void testExecMethod(Class<?> cls) throws ReflectiveOperationException {
+        try {
+            cls.getDeclaredMethod("execMethod").invoke(null);
+            throw new RuntimeException("InvocationTargetException not thrown");
+        } catch (InvocationTargetException e) {
+            // InvocationTargetException wraps the exception that was thrown while reflection.
+            Throwable t = e.getCause();
+            if (t instanceof ExceptionInInitializerError eiie) {
+                if (eiie.getCause() instanceof MyException) {
+                    return;
+                }
+                throw new RuntimeException("ExceptionInInitializerError due to other exception than MyException!", eiie);
+            }
+            throw new RuntimeException("InvocationTargetException was thrown not due to error while initialization!", e);
+        }
+    }
+
+    static void testFieldAccess(Class<?> cls) throws ReflectiveOperationException {
+        try {
+            cls.getDeclaredMethod("fieldAccess").invoke(null);
+            throw new RuntimeException("InvocationTargetException not thrown");
+        } catch (InvocationTargetException e) {
+            // the class initialization was run and failed.  NoClassDefFoundError
+            // should be thrown in this second attempt.
+            Throwable t = e.getCause();
+            if (t instanceof NoClassDefFoundError ncdfe) {
+                t = t.getCause();
+                if (t instanceof ExceptionInInitializerError eiie) {
+                    return;
+                }
+            }
+            throw new RuntimeException("InvocationTargetException was thrown not due to error while initialization!", e);
+        }
+    }
+
+}
diff --git a/test/jdk/java/lang/reflect/classInitialization/Initializer.java b/test/jdk/java/lang/reflect/classInitialization/Initializer.java
new file mode 100644
index 00000000000..072d14d509e
--- /dev/null
+++ b/test/jdk/java/lang/reflect/classInitialization/Initializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+class Initializer {
+    static void fieldAccess() throws Throwable {
+        Class<?> testedClass = Class.forName("Test", false, Initializer.class.getClassLoader());
+        testedClass.getDeclaredField("field").set(testedClass, 1);
+    }
+
+    static void execMethod() throws Throwable {
+        Class<?> testedClass = Class.forName("Test", false, Initializer.class.getClassLoader());
+        testedClass.getDeclaredMethod("method").invoke(null);
+    }
+}
diff --git a/test/jdk/java/lang/reflect/classInitialization/Test.java b/test/jdk/java/lang/reflect/classInitialization/Test.java
new file mode 100644
index 00000000000..1d5e4e52630
--- /dev/null
+++ b/test/jdk/java/lang/reflect/classInitialization/Test.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+class MyException extends RuntimeException {}
+
+class SuperTest {
+    static {
+        if (true) {
+            throw new MyException();
+        }
+    }
+}
+
+class Test extends SuperTest {
+
+    static int field = 0;
+
+    static void method() {};
+
+    static {
+        if (true) {
+            throw new OutOfMemoryError();
+        }
+    }
+}
diff --git a/test/jdk/jdk/internal/reflect/CallerSensitive/CheckCSMs.java b/test/jdk/jdk/internal/reflect/CallerSensitive/CheckCSMs.java
index cae2f84be61..35dfb8d19c3 100644
--- a/test/jdk/jdk/internal/reflect/CallerSensitive/CheckCSMs.java
+++ b/test/jdk/jdk/internal/reflect/CallerSensitive/CheckCSMs.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021, 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
@@ -22,6 +22,8 @@
  */
 
 import com.sun.tools.classfile.*;
+
+import static com.sun.tools.classfile.AccessFlags.ACC_PRIVATE;
 import static com.sun.tools.classfile.ConstantPool.*;
 import java.io.File;
 import java.io.IOException;
@@ -33,10 +35,13 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -62,13 +67,21 @@ public class CheckCSMs {
     // The goal is to remove this list of Non-final instance @CS methods
     // over time.  Do not add any new one to this list.
     private static Set<String> KNOWN_NON_FINAL_CSMS =
-      Set.of("java/io/ObjectStreamField#getType ()Ljava/lang/Class;",
-             "java/io/ObjectStreamClass#forClass ()Ljava/lang/Class;",
-             "java/lang/Runtime#load (Ljava/lang/String;)V",
-             "java/lang/Runtime#loadLibrary (Ljava/lang/String;)V",
-             "java/lang/Thread#getContextClassLoader ()Ljava/lang/ClassLoader;",
-             "javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;"
-      );
+        Set.of("java/io/ObjectStreamField#getType ()Ljava/lang/Class;",
+               "java/io/ObjectStreamClass#forClass ()Ljava/lang/Class;",
+               "java/lang/Runtime#load (Ljava/lang/String;)V",
+               "java/lang/Runtime#loadLibrary (Ljava/lang/String;)V",
+               "java/lang/Thread#getContextClassLoader ()Ljava/lang/ClassLoader;",
+               "javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;"
+        );
+
+    // These non-static non-final methods must not have @CallerSensitiveAdapter
+    // methods that takes an additional caller class parameter.
+    private static Set<String> UNSUPPORTED_VIRTUAL_METHODS =
+        Set.of("java/io/ObjectStreamField#getType (Ljava/lang/Class;)Ljava/lang/Class;",
+               "java/lang/Thread#getContextClassLoader (Ljava/lang/Class;)Ljava/lang/ClassLoader;",
+               "javax/sql/rowset/serial/SerialJavaObject#getFields (Ljava/lang/Class;)[Ljava/lang/reflect/Field;"
+        );
 
     public static void main(String[] args) throws Exception {
         if (args.length > 0 && args[0].equals("--list")) {
@@ -84,9 +97,19 @@ public static void main(String[] args) throws Exception {
                 result.stream().sorted()
                       .collect(Collectors.joining("\n", "\n", "")));
         }
+
+        // check if all csm methods with a trailing Class parameter are supported
+        checkCSMs.csmWithCallerParameter.values().stream()
+                 .flatMap(Set::stream)
+                 .forEach(m -> {
+                     if (UNSUPPORTED_VIRTUAL_METHODS.contains(m))
+                         throw new RuntimeException("Unsupported alternate csm adapter: " + m);
+                 });
     }
 
     private final Set<String> nonFinalCSMs = new ConcurrentSkipListSet<>();
+    private final Map<String, Set<String>> csmWithCallerParameter = new ConcurrentHashMap<>();
+
     private final ReferenceFinder finder;
     public CheckCSMs() {
         this.finder = new ReferenceFinder(getFilter(), getVisitor());
@@ -129,9 +152,7 @@ public void visit(ClassFile cf, Method m,  List<CPRefInfo> refs) {
                         m.getName(cf.constant_pool).equals("getCallerClass"))
                         return;
 
-                    String name = String.format("%s#%s %s", cf.getName(),
-                                                m.getName(cf.constant_pool),
-                                                m.descriptor.getValue(cf.constant_pool));
+                    String name = methodSignature(cf, m);
                     if (!CheckCSMs.isStaticOrFinal(cf, m, cf.constant_pool)) {
                         System.err.println("Unsupported @CallerSensitive: " + name);
                         nonFinalCSMs.add(name);
@@ -140,6 +161,15 @@ public void visit(ClassFile cf, Method m,  List<CPRefInfo> refs) {
                             System.out.format("@CS  %s%n", name);
                         }
                     }
+
+                    // find the adapter implementation for CSM with the caller parameter
+                    if (!csmWithCallerParameter.containsKey(cf.getName())) {
+                        Set<String> methods = Arrays.stream(cf.methods)
+                                                    .filter(m0 -> csmWithCallerParameter(cf, m, m0))
+                                                    .map(m0 -> methodSignature(cf, m0))
+                                                    .collect(Collectors.toSet());
+                        csmWithCallerParameter.put(cf.getName(), methods);
+                    }
                 } catch (ConstantPoolException ex) {
                     throw new RuntimeException(ex);
                 }
@@ -147,8 +177,58 @@ public void visit(ClassFile cf, Method m,  List<CPRefInfo> refs) {
         };
     }
 
+    private static String methodSignature(ClassFile cf, Method m) {
+        try {
+            return String.format("%s#%s %s", cf.getName(),
+                                 m.getName(cf.constant_pool),
+                                 m.descriptor.getValue(cf.constant_pool));
+        } catch (ConstantPoolException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private static boolean csmWithCallerParameter(ClassFile cf, Method csm, Method m) {
+        ConstantPool cp = cf.constant_pool;
+        try {
+            int csmParamCount = csm.descriptor.getParameterCount(cp);
+            int paramCount = m.descriptor.getParameterCount(cp);
+            // an adapter method must have the same name and return type and a trailing Class parameter
+            if (!(csm.getName(cp).equals(m.getName(cp)) &&
+                    paramCount == (csmParamCount+1) &&
+                    m.descriptor.getReturnType(cp).equals(csm.descriptor.getReturnType(cp)))) {
+                return false;
+            }
+            // the descriptor of the adapter method must have the parameters
+            // of the caller-sensitive method and an additional Class parameter
+            String csmDesc = csm.descriptor.getParameterTypes(cp);
+            String desc = m.descriptor.getParameterTypes(cp);
+            int index = desc.indexOf(", java.lang.Class)");
+            if (index == -1) {
+                index = desc.indexOf("java.lang.Class)");
+                if (index == -1) return false;
+            }
+            String s = desc.substring(0, index) + ")";
+            if (s.equals(csmDesc)) {
+                if (!m.access_flags.is(ACC_PRIVATE)) {
+                    throw new RuntimeException(methodSignature(cf, m) + " adapter method for " +
+                            methodSignature(cf, csm) + " must be private");
+                }
+                if (!isCallerSensitiveAdapter(m, cp)) {
+                    throw new RuntimeException(methodSignature(cf, m) + " adapter method for " +
+                            methodSignature(cf, csm) + " must be annotated with @CallerSensitiveAdapter");
+                }
+                return true;
+            }
+        } catch (ConstantPoolException|Descriptor.InvalidDescriptor e) {
+            throw new RuntimeException(e);
+        }
+        return false;
+    }
+
     private static final String CALLER_SENSITIVE_ANNOTATION
         = "Ljdk/internal/reflect/CallerSensitive;";
+    private static final String CALLER_SENSITIVE_ADAPTER_ANNOTATION
+        = "Ljdk/internal/reflect/CallerSensitiveAdapter;";
 
     private static boolean isCallerSensitive(Method m, ConstantPool cp)
         throws ConstantPoolException
@@ -167,6 +247,23 @@ private static boolean isCallerSensitive(Method m, ConstantPool cp)
         return false;
     }
 
+    private static boolean isCallerSensitiveAdapter(Method m, ConstantPool cp)
+            throws ConstantPoolException
+    {
+        RuntimeAnnotations_attribute attr =
+                (RuntimeAnnotations_attribute)m.attributes.get(Attribute.RuntimeInvisibleAnnotations);
+        if (attr != null) {
+            for (int i = 0; i < attr.annotations.length; i++) {
+                Annotation ann = attr.annotations[i];
+                String annType = cp.getUTF8Value(ann.type_index);
+                if (CALLER_SENSITIVE_ADAPTER_ANNOTATION.equals(annType)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private static boolean isStaticOrFinal(ClassFile cf, Method m, ConstantPool cp)
         throws ConstantPoolException
     {
diff --git a/test/jdk/jdk/internal/reflect/Reflection/GetCallerClass.java b/test/jdk/jdk/internal/reflect/Reflection/GetCallerClass.java
index 163a80ad088..697523c9b89 100644
--- a/test/jdk/jdk/internal/reflect/Reflection/GetCallerClass.java
+++ b/test/jdk/jdk/internal/reflect/Reflection/GetCallerClass.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021, 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
@@ -23,15 +23,69 @@
 
 package boot;
 
+import static java.lang.System.out;
+
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 public class GetCallerClass {
+
+    public Class<?> missingCallerSensitiveAnnotation() {
+        return jdk.internal.reflect.Reflection.getCallerClass();
+    }
+
+    @jdk.internal.reflect.CallerSensitive
+    public Class<?> getCallerClass() {
+        var caller = jdk.internal.reflect.Reflection.getCallerClass();
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
+    }
+
+    private Class<?> getCallerClass(Class<?> caller) {
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
+    }
+
     @jdk.internal.reflect.CallerSensitive
-    public ClassLoader getCallerLoader() {
-        Class<?> c = jdk.internal.reflect.Reflection.getCallerClass();
-        return c.getClassLoader();
+    public static Class<?> getCallerClassStatic() {
+        var caller = jdk.internal.reflect.Reflection.getCallerClass();
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
     }
 
-    public ClassLoader missingCallerSensitiveAnnotation() {
-        Class<?> c = jdk.internal.reflect.Reflection.getCallerClass();
-        return c.getClassLoader();
+    private static Class<?> getCallerClassStatic(Class<?> caller) {
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
+    }
+
+    @jdk.internal.reflect.CallerSensitive
+    public Class<?> getCallerClassNoAlt() {
+        var caller = jdk.internal.reflect.Reflection.getCallerClass();
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
+    }
+
+    @jdk.internal.reflect.CallerSensitive
+    public static Class<?> getCallerClassStaticNoAlt() {
+        var caller = jdk.internal.reflect.Reflection.getCallerClass();
+        out.println("caller: " + caller);
+        out.println(StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES).walk(toStackTrace()));
+        return caller;
+    }
+
+    private static Function<Stream<StackWalker.StackFrame>, String> toStackTrace() {
+        return frames -> frames
+            .takeWhile(
+                frame -> !frame.getClassName().equals("GetCallerClassTest") ||
+                         !frame.getMethodName().equals("main"))
+            .map(Object::toString)
+            .collect(Collectors.joining("\n  ", "  ", "\n"));
     }
 }
+
diff --git a/test/jdk/jdk/internal/reflect/Reflection/GetCallerClassTest.java b/test/jdk/jdk/internal/reflect/Reflection/GetCallerClassTest.java
index 2ba38c4b4bf..e3da595c782 100644
--- a/test/jdk/jdk/internal/reflect/Reflection/GetCallerClassTest.java
+++ b/test/jdk/jdk/internal/reflect/Reflection/GetCallerClassTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021, 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
@@ -25,33 +25,78 @@
  * @test
  * @bug 8010117
  * @summary Test if the VM enforces Reflection.getCallerClass
- *          be called by methods annotated with CallerSensitive
+ *          be called by system methods annotated with CallerSensitive plus
+ *          test reflective and method handle based invocation of caller-sensitive
+ *          methods with or without the CSM adapter method
  * @modules java.base/jdk.internal.reflect
  * @build SetupGetCallerClass boot.GetCallerClass
  * @run driver SetupGetCallerClass
- * @run main/othervm -Xbootclasspath/a:bcp GetCallerClassTest
+ * @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=true GetCallerClassTest
+ */
+
+/*
+ * @test
+ * @summary Verify the new NativeAccessor
+ * @modules java.base/jdk.internal.reflect
+ * @build SetupGetCallerClass boot.GetCallerClass
+ * @run driver SetupGetCallerClass
+ * @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=true -Djdk.reflect.useNativeAccessorOnly=true GetCallerClassTest
+ */
+
+/*
+ * @test
+ * @summary Verify NativeMethodAccessorImpl
+ * @modules java.base/jdk.internal.reflect
+ * @build SetupGetCallerClass boot.GetCallerClass
+ * @run driver SetupGetCallerClass
+ * @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=false -Dsun.reflect.noInflation=false GetCallerClassTest
+ */
+
+/*
+ * @test
+ * @summary Verify the old generated MethodAccessor
+ * @modules java.base/jdk.internal.reflect
+ * @build SetupGetCallerClass boot.GetCallerClass
+ * @run driver SetupGetCallerClass
+ * @run main/othervm -Xbootclasspath/a:bcp -Djdk.reflect.useDirectMethodHandle=false -Dsun.reflect.noInflation=true GetCallerClassTest
  */
 
 import boot.GetCallerClass;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
 import java.lang.reflect.*;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 
 public class GetCallerClassTest {
-    private final GetCallerClass gcc;   // boot.GetCallerClass is in bootclasspath
-    GetCallerClassTest() {
-        this.gcc = new GetCallerClass();
-    }
+    // boot.GetCallerClass is in bootclasspath
+    private static final Class<GetCallerClass> gccCl = GetCallerClass.class;
+    private final GetCallerClass gcc = new GetCallerClass();
 
     public static void main(String[] args) throws Exception {
         GetCallerClassTest gcct = new GetCallerClassTest();
-        // ensure methods are annotated with @CallerSensitive
-        ensureAnnotationPresent(boot.GetCallerClass.class, "getCallerLoader", true);
+        // ensure methods are annotated with @CallerSensitive and verify Reflection.isCallerSensitive()
         ensureAnnotationPresent(GetCallerClassTest.class, "testNonSystemMethod", false);
-        // call Reflection.getCallerClass from bootclasspath with and without @CS
-        gcct.testCallerSensitiveMethods();
+
+        ensureAnnotationPresent(gccCl, "getCallerClass", true);
+        ensureAnnotationPresent(gccCl, "getCallerClassStatic", true);
+        ensureAnnotationPresent(gccCl, "getCallerClassNoAlt", true);
+        ensureAnnotationPresent(gccCl, "getCallerClassStaticNoAlt", true);
+
+        // call Reflection.getCallerClass from bootclasspath without @CS
+        gcct.testMissingCallerSensitiveAnnotation();
         // call Reflection.getCallerClass from classpath with @CS
         gcct.testNonSystemMethod();
+        // call Reflection.getCallerClass from bootclasspath with @CS
+        gcct.testCallerSensitiveMethods();
+        // call @CS methods using reflection
+        gcct.testCallerSensitiveMethodsUsingReflection();
+        // call @CS methods using method handles
+        gcct.testCallerSensitiveMethodsUsingMethodHandles();
+        // call @CS methods using reflection but call Method.invoke with a method handle
+        gcct.testCallerSensitiveMethodsUsingMethodHandlesAndReflection();
     }
 
     private static void ensureAnnotationPresent(Class<?> c, String name, boolean cs)
@@ -67,58 +112,170 @@ private static void ensureAnnotationPresent(Class<?> c, String name, boolean cs)
         }
     }
 
-    private void testCallerSensitiveMethods() {
+    private void testMissingCallerSensitiveAnnotation() {
+        System.out.println("\ntestMissingCallerSensitiveAnnotation...");
         try {
-            ClassLoader cl = gcc.getCallerLoader();
-            if (cl != GetCallerClassTest.class.getClassLoader()) {
-                throw new RuntimeException("mismatched class loader");
-            }
             gcc.missingCallerSensitiveAnnotation();
-            throw new RuntimeException("getCallerLoader not marked with @CallerSensitive");
+            throw new RuntimeException("shouldn't have succeeded");
         } catch (InternalError e) {
-            StackTraceElement[] stackTrace = e.getStackTrace();
-            checkStackTrace(stackTrace, e);
-            if (!stackTrace[1].getClassName().equals(GetCallerClass.class.getName()) ||
-                !stackTrace[1].getMethodName().equals("missingCallerSensitiveAnnotation")) {
-                throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
-            }
-            if (!stackTrace[2].getClassName().equals(GetCallerClassTest.class.getName()) ||
-                !stackTrace[2].getMethodName().equals("testCallerSensitiveMethods")) {
-                throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
+            if (e.getMessage().startsWith("CallerSensitive annotation expected")) {
+                System.out.println("Expected error: " + e.getMessage());
+            } else {
+                throw e;
             }
-            System.out.println("Expected error: " + e.getMessage());
         }
     }
 
     @CallerSensitive
     private void testNonSystemMethod() {
+        System.out.println("\ntestNonSystemMethod...");
         try {
             Class<?> c = Reflection.getCallerClass();
-            throw new RuntimeException("@CallerSensitive testNonSystemMethods not supported");
+            throw new RuntimeException("shouldn't have succeeded");
         } catch (InternalError e) {
-            StackTraceElement[] stackTrace = e.getStackTrace();
-            checkStackTrace(stackTrace, e);
-            if (!stackTrace[1].getClassName().equals(GetCallerClassTest.class.getName()) ||
-                !stackTrace[1].getMethodName().equals("testNonSystemMethod")) {
-                throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
-            }
-            if (!stackTrace[2].getClassName().equals(GetCallerClassTest.class.getName()) ||
-                !stackTrace[2].getMethodName().equals("main")) {
-                throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
+            if (e.getMessage().startsWith("CallerSensitive annotation expected")) {
+                System.out.println("Expected error: " + e.getMessage());
+            } else {
+                throw e;
             }
-            System.out.println("Expected error: " + e.getMessage());
         }
     }
 
-    private void checkStackTrace(StackTraceElement[] stackTrace, Error e) {
-        if (stackTrace.length < 3) {
-            throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
+    private void testCallerSensitiveMethods() {
+        System.out.println();
+        Class<?> caller;
+
+        caller = gcc.getCallerClass();
+        if (caller != GetCallerClassTest.class) {
+            throw new RuntimeException("mismatched caller: " + caller);
+        }
+
+        caller = GetCallerClass.getCallerClassStatic();
+        if (caller != GetCallerClassTest.class) {
+            throw new RuntimeException("mismatched caller: " + caller);
+        }
+    }
+
+    private void testCallerSensitiveMethodsUsingReflection() {
+        System.out.println();
+
+        try {
+            Class<?> caller;
+
+            caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClass").invoke(gcc);
+            if (caller != GetCallerClassTest.class) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassStatic").invoke(null);
+            if (caller != GetCallerClassTest.class) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassNoAlt").invoke(gcc);
+            if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) gccCl.getDeclaredMethod("getCallerClassStaticNoAlt").invoke(null);
+            if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+        } catch (ReflectiveOperationException|SecurityException e) {
+            throw new RuntimeException(e);
         }
+    }
+
+    private void testCallerSensitiveMethodsUsingMethodHandles() {
+        System.out.println();
+
+        try {
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+            MethodType mt = MethodType.methodType(Class.class);
+            Class<?> caller;
+
+            caller = (Class<?>) lookup.findVirtual(gccCl, "getCallerClass", mt).invokeExact(gcc);
+            if (caller != GetCallerClassTest.class) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) lookup.findStatic(gccCl, "getCallerClassStatic", mt).invokeExact();
+            if (caller != GetCallerClassTest.class) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
 
-        if (!stackTrace[0].getClassName().equals("jdk.internal.reflect.Reflection") ||
-            !stackTrace[0].getMethodName().equals("getCallerClass")) {
-            throw new RuntimeException("Unexpected error: " + e.getMessage(), e);
+            caller = (Class<?>) lookup.findVirtual(gccCl, "getCallerClassNoAlt", mt).invokeExact(gcc);
+            if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) lookup.findStatic(gccCl, "getCallerClassStaticNoAlt", mt).invokeExact();
+            if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+        } catch (RuntimeException | Error e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
         }
+    }
+
+    private static final Object[] EMPTY_ARRAY = new Object[0];
+
+    private void testCallerSensitiveMethodsUsingMethodHandlesAndReflection() {
+        // In the old implementation, the caller returned is java.lang.invoke.Method
+        // since it looks up the caller through stack walking.
+        // The new implementation uses the special calling sequence and Method::invoke
+        // defines an adapter method such that the stack walking is done only once
+        // using the same caller class.
+        String s = System.getProperty("jdk.reflect.useDirectMethodHandle", "true");
+        boolean newImpl = Boolean.valueOf(s);
+        Class<?> expectedCaller = newImpl ? GetCallerClassTest.class : Method.class;
+
+        System.out.println();
+        try {
+            MethodHandle methodInvokeMh = MethodHandles
+                .lookup()
+                .findVirtual(Method.class, "invoke", MethodType.methodType(Object.class, Object.class, Object[].class));
+
+            Class<?> caller;
 
+            caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClass"), gcc, EMPTY_ARRAY);
+            if (caller != expectedCaller) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassStatic"), null, EMPTY_ARRAY);
+            if (caller != expectedCaller) {
+                throw new RuntimeException("mismatched caller: " + caller);
+            }
+
+            caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassNoAlt"), gcc, EMPTY_ARRAY);
+            if (newImpl) {
+                if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                    throw new RuntimeException("mismatched caller: " + caller);
+                }
+            } else {
+                if (caller != expectedCaller) {
+                    throw new RuntimeException("mismatched caller: " + caller);
+                }
+            }
+
+            caller = (Class<?>) methodInvokeMh.invoke(gccCl.getDeclaredMethod("getCallerClassStaticNoAlt"), null, EMPTY_ARRAY);
+            if (newImpl) {
+                if (!caller.isNestmateOf(GetCallerClassTest.class)) {
+                    throw new RuntimeException("mismatched caller: " + caller);
+                }
+            } else {
+                if (caller != expectedCaller) {
+                    throw new RuntimeException("mismatched caller: " + caller);
+                }
+            }
+        } catch (RuntimeException | Error e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
+        }
     }
 }
+
diff --git a/test/jdk/jdk/jfr/api/consumer/TestHiddenMethod.java b/test/jdk/jdk/jfr/api/consumer/TestHiddenMethod.java
index 08a9a68f3ac..7a5bf1e42c4 100644
--- a/test/jdk/jdk/jfr/api/consumer/TestHiddenMethod.java
+++ b/test/jdk/jdk/jfr/api/consumer/TestHiddenMethod.java
@@ -78,7 +78,9 @@ public Void run() {
             System.out.println("visibleEvent:" + visibleEvent);
 
             assertTrue(hasHiddenStackFrame(hiddenEvent), "No hidden frame in hidden event: " + hiddenEvent);
-            assertFalse(hasHiddenStackFrame(visibleEvent), "Hidden frame in visible event: " + visibleEvent);
+
+            // Temporary disable this test until JDK-8272064 is resolved.
+            // assertFalse(hasHiddenStackFrame(visibleEvent), "Hidden frame in visible event: " + visibleEvent);
         }
     }
 
diff --git a/test/langtools/jdk/jshell/ExceptionsTest.java b/test/langtools/jdk/jshell/ExceptionsTest.java
index ed4a5ed44bd..765c3696f07 100644
--- a/test/langtools/jdk/jshell/ExceptionsTest.java
+++ b/test/langtools/jdk/jshell/ExceptionsTest.java
@@ -245,6 +245,7 @@ public void throwFromNoSource() {
                 new ExceptionInfo(ExceptionInInitializerError.class, null,
                         new StackTraceElement("java.lang.Class", "forName0",  "Class.java", -2),
                         new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
+                        new StackTraceElement("java.lang.Class", "forName", "Class.java", -2),
                         newStackTraceElement("", "", se.snippet(), 1)));
     }
 
diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionColdstartBenchmark.java b/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionColdstartBenchmark.java
index 743e8842dbb..ff3259547ff 100644
--- a/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionColdstartBenchmark.java
+++ b/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionColdstartBenchmark.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -42,7 +42,7 @@
 @BenchmarkMode(Mode.SingleShotTime)
 @OutputTimeUnit(TimeUnit.MICROSECONDS)
 @State(Scope.Benchmark)
-@Fork(value = 30, warmups = 10)
+@Fork(value = 10, warmups = 0)
 public class ReflectionColdstartBenchmark {
 
     static class Nested {
@@ -121,9 +121,22 @@ public void setup() {
     }
 
     @Benchmark
-    public void invokeMethods(Blackhole bh) throws ReflectiveOperationException {
+    public void invokeMethods() throws ReflectiveOperationException {
+        // As this is testing warmup, JITs are unlikely to progress to the
+        // point where the lack of a blackhole might lead to DCE. Omitting it
+        // makes it easier to test this code minimal use of JMH code
         for (Method m : methods) {
-            bh.consume(m.invoke(null, arg));
+            m.invoke(null, arg);
         }
     }
+
+    /**
+     * Runs invokeMethods once without any JMH interaction, acting as an
+     * independent startup benchmark.
+     */
+    public static void main(String ... args) throws Exception {
+        var coldstart = new ReflectionColdstartBenchmark();
+        coldstart.setup();
+        coldstart.invokeMethods();
+    }
 }
diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionSpeedBenchmark.java b/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionSpeedBenchmark.java
index c2cdbb4cc21..575f234b509 100644
--- a/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionSpeedBenchmark.java
+++ b/test/micro/org/openjdk/bench/java/lang/reflect/ReflectionSpeedBenchmark.java
@@ -24,11 +24,13 @@
 
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
 import org.openjdk.jmh.annotations.Fork;
 import org.openjdk.jmh.annotations.Level;
 import org.openjdk.jmh.annotations.Measurement;
 import org.openjdk.jmh.annotations.Mode;
 import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.Setup;
 import org.openjdk.jmh.annotations.State;
@@ -43,9 +45,9 @@
 /**
  * Benchmark measuring field access and method invocation using different conditions:
  * <ul>
- *     <li>Const - Method/Field is constant-foldable</li>
- *     <li>Var - Method/Field is single-instance but not constant-foldable</li>
- *     <li>Poly - multiple Method/Field instances used at single call-site</li>
+ *     <li>Const - Constructor/Method/Field is constant-foldable</li>
+ *     <li>Var - Constructor/Method/Field is single-instance but not constant-foldable</li>
+ *     <li>Poly - multiple Constructor/Method/Field instances used at single call-site</li>
  * </ul>
  */
 @BenchmarkMode(Mode.AverageTime)
@@ -78,8 +80,8 @@ public class ReflectionSpeedBenchmark {
 
     static {
         try {
-            staticMethodConst = staticMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumStatic", int.class, int.class);
-            instanceMethodConst = instanceMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumInstance", int.class, int.class);
+            staticMethodConst = staticMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumStatic", Integer.class, Integer.class);
+            instanceMethodConst = instanceMethodVar = ReflectionSpeedBenchmark.class.getDeclaredMethod("sumInstance", Integer.class, Integer.class);
 
             staticFieldConst = staticFieldVar = ReflectionSpeedBenchmark.class.getDeclaredField("staticField");
             instanceFieldConst = instanceFieldVar = ReflectionSpeedBenchmark.class.getDeclaredField("instanceField");
@@ -268,7 +270,7 @@ public NestedConstruction(Void p1, Void p2, Void p3, Void p4, Void p5, Void p6,
     }
 
     private int rnd = 0;
-    private int a, b;
+    private Integer a, b;
     private Object o;
     private NestedInstance instance;
 
@@ -276,58 +278,126 @@ private int nextRnd() {
         return rnd += 7;
     }
 
+    // @Param({"true", "false"})
+    private boolean polluteProfile = true;
+
     @Setup(Level.Iteration)
     public void setup() {
         a = nextRnd();
         b = nextRnd();
         o = new Object();
         instance = new NestedInstance();
+
+        if (polluteProfile) {
+            try {
+                Constructor ctor = ReflectionSpeedBenchmark.class.getDeclaredConstructor(Integer.class);
+                Method test1 = ReflectionSpeedBenchmark.class.getDeclaredMethod("test1", Object.class);
+                Method test2 = ReflectionSpeedBenchmark.class.getDeclaredMethod("test2", Object.class, Object.class);
+                Field f = ReflectionSpeedBenchmark.class.getDeclaredField("testField");
+                for (int i = 0; i < 20_000; i++) {
+                    invokeHelper2(staticMethodVar, null, a, b);
+                    invokeHelper2(instanceMethodVar, this, a, b);
+                    invokeHelper2(test2, null, a, b);
+                    invokeHelper1(staticMethodsPoly[i & (staticMethodsPoly.length - 1)], instance, o);
+                    invokeHelper1(instanceMethodsPoly[i & (instanceMethodsPoly.length - 1)], instance, o);
+                    invokeHelper1(test1, null, a);
+
+                    newInstanceHelper(constructorVar, constructorArgs);
+                    int index = i & (constructorsPoly.length - 1);
+                    newInstanceHelper(constructorsPoly[index], constructorsArgsPoly[index]);
+                    newInstanceHelper(ctor, new Object[]{a});
+
+                    getIntHelper(staticFieldVar, null);
+                    getIntHelper(instanceFieldVar, this);
+                    getDoubleHelper(f, null);
+                    getHelper(staticFieldsPoly[i & (staticFieldsPoly.length - 1)], null);
+                    getHelper(instanceFieldsPoly[i & (instanceFieldsPoly.length - 1)], instance);
+                }
+            } catch (ReflectiveOperationException e) {
+                 throw new InternalError(e);
+            }
+        }
     }
 
-    public static int sumStatic(int a, int b) {
-        return a + b;
+    public static Integer sumStatic(Integer a, Integer b) {
+        return a; // a + b;
     }
 
-    public int sumInstance(int a, int b) {
-        return a + b;
+    public Integer sumInstance(Integer a, Integer b) {
+        return a; // a + b;
     }
 
     public static int staticField;
     public int instanceField;
+    public ReflectionSpeedBenchmark() {}
+
+    // used for polluting the profile
+    private ReflectionSpeedBenchmark(Integer a) {}
+    static void test1(Object a) {}
+    static void test2(Object a, Object b) {}
+    static double testField;
 
     // methods
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static Object invokeHelper1(Method m, Object recv, Object arg1) throws InvocationTargetException, IllegalAccessException {
+        return m.invoke(recv, arg1);
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static Object invokeHelper2(Method m, Object recv, Object arg1, Object arg2) throws InvocationTargetException, IllegalAccessException {
+        return m.invoke(recv, arg1, arg2);
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static Object newInstanceHelper(Constructor ctor, Object[] args) throws InvocationTargetException, IllegalAccessException, InstantiationException {
+        return ctor.newInstance(args);
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static int getIntHelper(Field f, Object recv) throws IllegalAccessException {
+        return f.getInt(recv);
+    }
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static double getDoubleHelper(Field f, Object recv) throws IllegalAccessException {
+        return f.getDouble(recv);
+    }
+
+    @CompilerControl(CompilerControl.Mode.INLINE)
+    static Object getHelper(Field f, Object recv) throws IllegalAccessException {
+        return f.get(recv);
+    }
 
     @Benchmark
-    public int staticMethodConst() {
+    public Object staticMethodConst() {
         try {
-            return (Integer) staticMethodConst.invoke(null, a, b);
+            return invokeHelper2(staticMethodConst, null, a, b);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
     }
 
     @Benchmark
-    public int instanceMethodConst() {
+    public Object instanceMethodConst() {
         try {
-            return (Integer) instanceMethodConst.invoke(this, a, b);
+            return invokeHelper2(instanceMethodConst, this, a, b);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
     }
 
     @Benchmark
-    public int staticMethodVar() {
+    public Object staticMethodVar() {
         try {
-            return (Integer) staticMethodVar.invoke(null, a, b);
+            return invokeHelper2(staticMethodVar, null, a, b);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
     }
 
     @Benchmark
-    public int instanceMethodVar() {
+    public Object instanceMethodVar() {
         try {
-            return (Integer) instanceMethodVar.invoke(this, a, b);
+            return invokeHelper2(instanceMethodVar, this, a, b);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
@@ -336,7 +406,7 @@ public int instanceMethodVar() {
     @Benchmark
     public Object staticMethodPoly() {
         try {
-            return staticMethodsPoly[nextRnd() & (staticMethodsPoly.length - 1)].invoke(null, o);
+            return invokeHelper1(staticMethodsPoly[nextRnd() & (staticMethodsPoly.length - 1)], null, o);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
@@ -345,7 +415,7 @@ public Object staticMethodPoly() {
     @Benchmark
     public Object instanceMethodPoly() {
         try {
-            return instanceMethodsPoly[nextRnd() & (instanceMethodsPoly.length - 1)].invoke(instance, o);
+            return invokeHelper1(instanceMethodsPoly[nextRnd() & (instanceMethodsPoly.length - 1)], instance, o);
         } catch (IllegalAccessException | InvocationTargetException e) {
             throw new AssertionError(e);
         }
@@ -356,7 +426,7 @@ public Object instanceMethodPoly() {
     @Benchmark
     public int staticFieldConst() {
         try {
-            return staticFieldConst.getInt(null);
+            return getIntHelper(staticFieldConst, null);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -365,7 +435,7 @@ public int staticFieldConst() {
     @Benchmark
     public int instanceFieldConst() {
         try {
-            return instanceFieldConst.getInt(this);
+            return getIntHelper(instanceFieldConst, this);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -374,7 +444,7 @@ public int instanceFieldConst() {
     @Benchmark
     public int staticFieldVar() {
         try {
-            return staticFieldVar.getInt(null);
+            return getIntHelper(staticFieldVar, null);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -383,7 +453,7 @@ public int staticFieldVar() {
     @Benchmark
     public int instanceFieldVar() {
         try {
-            return instanceFieldVar.getInt(this);
+            return getIntHelper(instanceFieldVar, this);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -392,7 +462,7 @@ public int instanceFieldVar() {
     @Benchmark
     public Object staticFieldPoly() {
         try {
-            return staticFieldsPoly[nextRnd() & (staticFieldsPoly.length - 1)].get(null);
+            return getHelper(staticFieldsPoly[nextRnd() & (staticFieldsPoly.length - 1)], null);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -401,7 +471,7 @@ public Object staticFieldPoly() {
     @Benchmark
     public Object instanceFieldPoly() {
         try {
-            return instanceFieldsPoly[nextRnd() & (instanceFieldsPoly.length - 1)].get(instance);
+            return getHelper(instanceFieldsPoly[nextRnd() & (instanceFieldsPoly.length - 1)], instance);
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);
         }
@@ -412,7 +482,7 @@ public Object instanceFieldPoly() {
     @Benchmark
     public Object constructorConst() {
         try {
-            return constructorConst.newInstance(constructorArgs);
+            return newInstanceHelper(constructorConst, constructorArgs);
         } catch (ReflectiveOperationException e) {
             throw new AssertionError(e);
         }
@@ -421,7 +491,7 @@ public Object constructorConst() {
     @Benchmark
     public Object constructorVar() {
         try {
-            return constructorVar.newInstance(constructorArgs);
+            return newInstanceHelper(constructorVar, constructorArgs);
         } catch (ReflectiveOperationException e) {
             throw new AssertionError(e);
         }
@@ -431,9 +501,9 @@ public Object constructorVar() {
     public Object constructorPoly() {
         try {
             int i = nextRnd() & (constructorsPoly.length - 1);
-            return constructorsPoly[i].newInstance(constructorsArgsPoly[i]);
+            return newInstanceHelper(constructorsPoly[i], constructorsArgsPoly[i]);
         } catch (ReflectiveOperationException e) {
             throw new AssertionError(e);
         }
     }
-}
\ No newline at end of file
+}