diff --git a/core/src/com/google/inject/internal/InternalFlags.java b/core/src/com/google/inject/internal/InternalFlags.java
index 97d8d3e337..26a1e0e27b 100644
--- a/core/src/com/google/inject/internal/InternalFlags.java
+++ b/core/src/com/google/inject/internal/InternalFlags.java
@@ -58,13 +58,13 @@ public enum IncludeStackTraceOption {
public enum CustomClassLoadingOption {
/**
* Define fast/enhanced types in the same class loader as their original type, never creates
- * class loaders. Uses Unsafe.defineAnonymousClass to gain access to existing class loaders.
+ * class loaders. Uses {@link sun.misc.Unsafe} to gain access to existing class loaders.
*/
OFF,
/**
- * Define fast/enhanced types with Unsafe.defineAnonymousClass, never creates class loaders.
- * This is faster than regular class loading and anonymous classes are easier to unload.
+ * Define fast/enhanced types anonymously as hidden nest-mates, never creates class loaders.
+ * This is faster than regular class loading and the resulting classes are easier to unload.
*
* Note: with this option you cannot look up fast/enhanced types by name or mock/spy them.
*/
diff --git a/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java b/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java
new file mode 100644
index 0000000000..87c47a6ba6
--- /dev/null
+++ b/core/src/com/google/inject/internal/aop/AnonymousClassDefiner.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.internal.aop;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * {@link ClassDefiner} that defines classes using {@code sun.misc.Unsafe#defineAnonymousClass}.
+ *
+ * @author mcculls@gmail.com (Stuart McCulloch)
+ */
+final class AnonymousClassDefiner implements ClassDefiner {
+
+ private static final Object THE_UNSAFE;
+ private static final Method ANONYMOUS_DEFINE_METHOD;
+
+ static {
+ try {
+ Class> unsafeType = Class.forName("sun.misc.Unsafe");
+ Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe");
+ theUnsafeField.setAccessible(true);
+ THE_UNSAFE = theUnsafeField.get(null);
+ ANONYMOUS_DEFINE_METHOD =
+ unsafeType.getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class);
+ } catch (ReflectiveOperationException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ @Override
+ public Class> define(Class> hostClass, byte[] bytecode) throws Exception {
+ return (Class>) ANONYMOUS_DEFINE_METHOD.invoke(THE_UNSAFE, hostClass, bytecode, null);
+ }
+}
diff --git a/core/src/com/google/inject/internal/aop/ClassDefining.java b/core/src/com/google/inject/internal/aop/ClassDefining.java
index 7b8958a1b4..9ac662c29a 100644
--- a/core/src/com/google/inject/internal/aop/ClassDefining.java
+++ b/core/src/com/google/inject/internal/aop/ClassDefining.java
@@ -44,14 +44,19 @@ public static Class> define(Class> hostClass, byte[] bytecode) throws Except
return ClassDefinerHolder.INSTANCE.define(hostClass, bytecode);
}
- /** Returns true if the ClassDefiner has access to package-private members. */
+ /** Returns true if the current class definer allows access to package-private members. */
public static boolean hasPackageAccess() {
return ClassDefinerHolder.IS_UNSAFE;
}
- /** Does the given class host new types anonymously, meaning they are not visible by name? */
- public static boolean isAnonymousHost(Class> hostClass) {
- return ClassDefinerHolder.IS_UNSAFE && UnsafeClassDefiner.isAnonymousHost(hostClass);
+ /** Returns true if it's possible to load by name proxies defined from the given host. */
+ public static boolean canLoadProxyByName(Class> hostClass) {
+ return !ClassDefinerHolder.IS_UNSAFE || UnsafeClassDefiner.canLoadProxyByName(hostClass);
+ }
+
+ /** Returns true if it's possible to downcast to proxies defined from the given host. */
+ public static boolean canDowncastToProxy(Class> hostClass) {
+ return !ClassDefinerHolder.IS_UNSAFE || UnsafeClassDefiner.canDowncastToProxy(hostClass);
}
/** Binds the preferred {@link ClassDefiner} instance. */
diff --git a/core/src/com/google/inject/internal/aop/Enhancer.java b/core/src/com/google/inject/internal/aop/Enhancer.java
index 3c172287e1..c3bc63c26d 100644
--- a/core/src/com/google/inject/internal/aop/Enhancer.java
+++ b/core/src/com/google/inject/internal/aop/Enhancer.java
@@ -167,8 +167,8 @@ final class Enhancer extends AbstractGlueGenerator {
super(hostClass, ENHANCER_BY_GUICE_MARKER);
this.bridgeDelegates = bridgeDelegates;
- // CHECKCAST(proxyName) fails when hosted anonymously; hostName works in that scenario
- this.checkcastToProxy = ClassDefining.isAnonymousHost(hostClass) ? hostName : proxyName;
+ // with defineAnonymousClass we can't downcast to the proxy and must use host instead
+ this.checkcastToProxy = ClassDefining.canDowncastToProxy(hostClass) ? proxyName : hostName;
}
@Override
@@ -222,11 +222,8 @@ private void setupInvokerTable(ClassWriter cw) {
Handle trampolineHandle =
new Handle(H_INVOKESTATIC, proxyName, TRAMPOLINE_NAME, TRAMPOLINE_DESCRIPTOR, false);
- if (ClassDefining.isAnonymousHost(hostClass)) {
- // proxy class is anonymous we can't create our lambda glue, store raw trampoline instead
- mv.visitLdcInsn(trampolineHandle);
- } else {
- // otherwise generate lambda glue to make the raw trampoline look like an invoker table
+ if (ClassDefining.canLoadProxyByName(hostClass)) {
+ // generate lambda glue to make the raw trampoline look like an invoker table
mv.visitMethodInsn(
INVOKESTATIC,
@@ -254,6 +251,10 @@ private void setupInvokerTable(ClassWriter cw) {
"getTarget",
"()Ljava/lang/invoke/MethodHandle;",
false);
+
+ } else {
+ // proxy class is hidden so we can't create our lambda glue, store raw trampoline instead
+ mv.visitLdcInsn(trampolineHandle);
}
mv.visitFieldInsn(PUTSTATIC, proxyName, INVOKERS_NAME, INVOKERS_DESCRIPTOR);
diff --git a/core/src/com/google/inject/internal/aop/GeneratedClassDefiner.java b/core/src/com/google/inject/internal/aop/GeneratedClassDefiner.java
new file mode 100644
index 0000000000..65465c871a
--- /dev/null
+++ b/core/src/com/google/inject/internal/aop/GeneratedClassDefiner.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.internal.aop;
+
+import java.util.function.BiFunction;
+
+/**
+ * {@link ClassDefiner} that defines classes using a generated access function.
+ *
+ * @author mcculls@gmail.com (Stuart McCulloch)
+ */
+final class GeneratedClassDefiner implements ClassDefiner {
+
+ private final BiFunction> defineAccess;
+
+ GeneratedClassDefiner(BiFunction> defineAccess) {
+ this.defineAccess = defineAccess;
+ }
+
+ @Override
+ public Class> define(Class> hostClass, byte[] bytecode) throws Exception {
+ return defineAccess.apply(hostClass.getClassLoader(), bytecode);
+ }
+}
diff --git a/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java b/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java
new file mode 100644
index 0000000000..8e95263752
--- /dev/null
+++ b/core/src/com/google/inject/internal/aop/HiddenClassDefiner.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.inject.internal.aop;
+
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * {@link ClassDefiner} that defines classes using {@code MethodHandles.Lookup#defineHiddenClass}.
+ *
+ * @author mcculls@gmail.com (Stuart McCulloch)
+ */
+final class HiddenClassDefiner implements ClassDefiner {
+
+ private static final Object THE_UNSAFE;
+ private static final Object TRUSTED_LOOKUP_BASE;
+ private static final Object TRUSTED_LOOKUP_OFFSET;
+ private static final Method GET_OBJECT_METHOD;
+ private static final Object HIDDEN_CLASS_OPTIONS;
+ private static final Method HIDDEN_DEFINE_METHOD;
+
+ static {
+ try {
+ Class> unsafeType = Class.forName("sun.misc.Unsafe");
+ Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe");
+ theUnsafeField.setAccessible(true);
+ THE_UNSAFE = theUnsafeField.get(null);
+ Field trustedLookupField = Lookup.class.getDeclaredField("IMPL_LOOKUP");
+ Method baseMethod = unsafeType.getMethod("staticFieldBase", Field.class);
+ TRUSTED_LOOKUP_BASE = baseMethod.invoke(THE_UNSAFE, trustedLookupField);
+ Method offsetMethod = unsafeType.getMethod("staticFieldOffset", Field.class);
+ TRUSTED_LOOKUP_OFFSET = offsetMethod.invoke(THE_UNSAFE, trustedLookupField);
+ GET_OBJECT_METHOD = unsafeType.getMethod("getObject", Object.class, long.class);
+ HIDDEN_CLASS_OPTIONS = classOptions("NESTMATE");
+ HIDDEN_DEFINE_METHOD =
+ Lookup.class.getMethod(
+ "defineHiddenClass", byte[].class, boolean.class, HIDDEN_CLASS_OPTIONS.getClass());
+ } catch (ReflectiveOperationException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ @Override
+ public Class> define(Class> hostClass, byte[] bytecode) throws Exception {
+ Lookup trustedLookup =
+ (Lookup) GET_OBJECT_METHOD.invoke(THE_UNSAFE, TRUSTED_LOOKUP_BASE, TRUSTED_LOOKUP_OFFSET);
+ Lookup definedLookup =
+ (Lookup)
+ HIDDEN_DEFINE_METHOD.invoke(
+ trustedLookup.in(hostClass), bytecode, false, HIDDEN_CLASS_OPTIONS);
+ return definedLookup.lookupClass();
+ }
+
+ /** Creates {@link MethodHandles.Lookup.ClassOption} array with the named options. */
+ @SuppressWarnings("unchecked")
+ private static Object classOptions(String... options) throws ClassNotFoundException {
+ @SuppressWarnings("rawtypes") // Unavoidable, only way to use Enum.valueOf
+ Class optionClass = Class.forName(Lookup.class.getName() + "$ClassOption");
+ Object classOptions = Array.newInstance(optionClass, options.length);
+ for (int i = 0; i < options.length; i++) {
+ Array.set(classOptions, i, Enum.valueOf(optionClass, options[i]));
+ }
+ return classOptions;
+ }
+}
diff --git a/core/src/com/google/inject/internal/aop/UnsafeClassDefiner.java b/core/src/com/google/inject/internal/aop/UnsafeClassDefiner.java
index 56146319d9..dbb40cb6fc 100644
--- a/core/src/com/google/inject/internal/aop/UnsafeClassDefiner.java
+++ b/core/src/com/google/inject/internal/aop/UnsafeClassDefiner.java
@@ -17,15 +17,17 @@
package com.google.inject.internal.aop;
import static java.lang.reflect.Modifier.PUBLIC;
-import static java.lang.reflect.Modifier.STATIC;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
import com.google.common.cache.CacheBuilder;
@@ -33,14 +35,14 @@
import com.google.common.cache.LoadingCache;
import com.google.inject.internal.InternalFlags;
import com.google.inject.internal.InternalFlags.CustomClassLoadingOption;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
+import java.util.function.BiFunction;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
/**
* {@link ClassDefiner} that defines classes using {@code sun.misc.Unsafe}.
@@ -51,86 +53,78 @@ final class UnsafeClassDefiner implements ClassDefiner {
private static final Logger logger = Logger.getLogger(UnsafeClassDefiner.class.getName());
- private static final Object THE_UNSAFE =
- tryPrivileged(UnsafeClassDefiner::bindTheUnsafe, "Cannot bind the Unsafe instance");
+ private static final ClassDefiner UNSAFE_DEFINER;
- private static final Method ANONYMOUS_DEFINE_METHOD =
- tryPrivileged(
- UnsafeClassDefiner::bindAnonymousDefineMethod, "Cannot bind Unsafe.defineAnonymousClass");
+ static {
+ ClassDefiner unsafeDefiner =
+ tryPrivileged(AnonymousClassDefiner::new, "Cannot bind Unsafe.defineAnonymousClass");
+ if (unsafeDefiner == null) {
+ unsafeDefiner =
+ tryPrivileged(
+ HiddenClassDefiner::new, "Cannot bind MethodHandles.Lookup.defineHiddenClass");
+ }
+ UNSAFE_DEFINER = unsafeDefiner;
+ }
private static final boolean ALWAYS_DEFINE_ANONYMOUSLY =
InternalFlags.getCustomClassLoadingOption() == CustomClassLoadingOption.ANONYMOUS;
private static final String DEFINEACCESS_BY_GUICE_MARKER = "$$DefineAccessByGuice$$";
+ private static final String[] DEFINEACCESS_API = {"java/util/function/BiFunction"};
+
+ private static final String CLASS_LOADER_TYPE = Type.getInternalName(ClassLoader.class);
+
+ private static final String BYTE_ARRAY_TYPE = Type.getInternalName(byte[].class);
+
// initialization-on-demand...
- private static class ClassLoaderDefineMethodHolder {
- static final Method CLASS_LOADER_DEFINE_METHOD =
+ private static class ClassLoaderDefineClassHolder {
+ static final ClassDefiner CLASS_LOADER_DEFINE_CLASS =
tryPrivileged(
- () -> accessDefineMethod(ClassLoader.class), "Cannot access ClassLoader.defineClass");
+ () -> accessDefineClass(ClassLoader.class), "Cannot access ClassLoader.defineClass");
}
// initialization-on-demand...
- private static class DefineMethodCacheHolder {
- static final LoadingCache, Method> DEFINE_METHOD_CACHE =
+ private static class DefineClassCacheHolder {
+ static final LoadingCache, ClassDefiner> DEFINE_CLASS_CACHE =
CacheBuilder.newBuilder()
.weakKeys()
- .weakValues()
- .build(CacheLoader.from(UnsafeClassDefiner::tryAccessDefineMethod));
+ .build(CacheLoader.from(UnsafeClassDefiner::tryAccessDefineClass));
}
/** Do we have access to {@code sun.misc.Unsafe}? */
public static boolean isAccessible() {
- return ANONYMOUS_DEFINE_METHOD != null;
+ return UNSAFE_DEFINER != null;
+ }
+
+ /** Returns true if it's possible to load by name proxies defined from the given host. */
+ public static boolean canLoadProxyByName(Class> hostClass) {
+ return findClassDefiner(hostClass.getClassLoader()) != UNSAFE_DEFINER;
}
- /** Does the given class host new types anonymously, ie. by using defineAnonymousClass? */
- @SuppressWarnings("ReferenceEquality") // intentional
- public static boolean isAnonymousHost(Class> hostClass) {
- return findDefineMethod(hostClass.getClassLoader()) == ANONYMOUS_DEFINE_METHOD;
+ /** Returns true if it's possible to downcast to proxies defined from the given host. */
+ public static boolean canDowncastToProxy(Class> hostClass) {
+ return !(findClassDefiner(hostClass.getClassLoader()) instanceof AnonymousClassDefiner);
}
- @SuppressWarnings("ReferenceEquality") // intentional
@Override
public Class> define(Class> hostClass, byte[] bytecode) throws Exception {
- ClassLoader hostLoader = hostClass.getClassLoader();
- Method defineMethod = findDefineMethod(hostLoader);
- if (defineMethod == ANONYMOUS_DEFINE_METHOD) {
- return defineAnonymously(hostClass, bytecode);
- }
- return (Class>) defineMethod.invoke(null, hostLoader, bytecode);
+ return findClassDefiner(hostClass.getClassLoader()).define(hostClass, bytecode);
}
- /** Finds the appropriate class defining method for the given class loader. */
- private static Method findDefineMethod(ClassLoader hostLoader) {
+ /** Finds the appropriate class definer for the given class loader. */
+ private static ClassDefiner findClassDefiner(ClassLoader hostLoader) {
if (hostLoader == null || ALWAYS_DEFINE_ANONYMOUSLY) {
- return ANONYMOUS_DEFINE_METHOD;
- } else if (ClassLoaderDefineMethodHolder.CLASS_LOADER_DEFINE_METHOD != null) {
+ return UNSAFE_DEFINER;
+ } else if (ClassLoaderDefineClassHolder.CLASS_LOADER_DEFINE_CLASS != null) {
// we have access to the defineClass method of anything extending ClassLoader
- return ClassLoaderDefineMethodHolder.CLASS_LOADER_DEFINE_METHOD;
+ return ClassLoaderDefineClassHolder.CLASS_LOADER_DEFINE_CLASS;
} else {
// can't access ClassLoader, try accessing the specific sub-class instead
- return DefineMethodCacheHolder.DEFINE_METHOD_CACHE.getUnchecked(hostLoader.getClass());
+ return DefineClassCacheHolder.DEFINE_CLASS_CACHE.getUnchecked(hostLoader.getClass());
}
}
- private static Class> defineAnonymously(Class> hostClass, byte[] bytecode) throws Exception {
- return (Class>) ANONYMOUS_DEFINE_METHOD.invoke(THE_UNSAFE, hostClass, bytecode, null);
- }
-
- private static Object bindTheUnsafe() throws Exception {
- Class> unsafeType = Class.forName("sun.misc.Unsafe");
- Field theUnsafeField = unsafeType.getDeclaredField("theUnsafe");
- theUnsafeField.setAccessible(true);
- return theUnsafeField.get(null);
- }
-
- private static Method bindAnonymousDefineMethod() throws Exception {
- Class> unsafeType = THE_UNSAFE.getClass();
- // defineAnonymousClass has all the functionality we need and is available in Java 7 onwards
- return unsafeType.getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class);
- }
-
static T tryPrivileged(PrivilegedExceptionAction action, String errorMessage) {
try {
return AccessController.doPrivileged(action);
@@ -140,22 +134,24 @@ static T tryPrivileged(PrivilegedExceptionAction action, String errorMess
}
}
- static Method tryAccessDefineMethod(Class> loaderClass) {
+ static ClassDefiner tryAccessDefineClass(Class> loaderClass) {
try {
logger.log(Level.FINE, "Accessing defineClass method in %s", loaderClass);
return AccessController.doPrivileged(
- (PrivilegedExceptionAction) () -> accessDefineMethod(loaderClass));
+ (PrivilegedExceptionAction) () -> accessDefineClass(loaderClass));
} catch (Throwable e) {
logger.log(Level.FINE, "Cannot access defineClass method in " + loaderClass, e);
- return ANONYMOUS_DEFINE_METHOD;
+ return UNSAFE_DEFINER;
}
}
/** Generates helper in same package as the {@link ClassLoader} so it can access defineClass */
- static Method accessDefineMethod(Class> loaderClass) throws Exception {
+ @SuppressWarnings("unchecked")
+ static ClassDefiner accessDefineClass(Class> loaderClass) throws Exception {
byte[] bytecode = buildDefineClassAccess(loaderClass);
- Class> accessClass = defineAnonymously(loaderClass, bytecode);
- return accessClass.getMethod("defineClass", ClassLoader.class, byte[].class);
+ Class> accessClass = UNSAFE_DEFINER.define(loaderClass, bytecode);
+ return new GeneratedClassDefiner(
+ (BiFunction>) accessClass.newInstance());
}
/** {@link ClassLoader} helper that sits in the same package and passes on defineClass requests */
@@ -169,23 +165,36 @@ private static byte[] buildDefineClassAccess(Class> loaderClass) {
loaderClass.getName().replace('.', '/') + DEFINEACCESS_BY_GUICE_MARKER,
null,
"java/lang/Object",
- null);
+ DEFINEACCESS_API);
+
+ MethodVisitor mv;
+
+ mv = cw.visitMethod(PUBLIC, "", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
- MethodVisitor mv =
+ mv =
cw.visitMethod(
- PUBLIC | STATIC,
- "defineClass",
- "(Ljava/lang/ClassLoader;[B)Ljava/lang/Class;",
+ PUBLIC,
+ "apply",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
null,
null);
mv.visitCode();
- mv.visitVarInsn(ALOAD, 0);
- mv.visitInsn(ACONST_NULL);
mv.visitVarInsn(ALOAD, 1);
+ mv.visitTypeInsn(CHECKCAST, CLASS_LOADER_TYPE);
+ mv.visitInsn(ACONST_NULL);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, BYTE_ARRAY_TYPE);
mv.visitInsn(ICONST_0);
- mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitTypeInsn(CHECKCAST, BYTE_ARRAY_TYPE);
mv.visitInsn(ARRAYLENGTH);
mv.visitMethodInsn(
diff --git a/core/test/com/google/inject/ImplicitBindingTest.java b/core/test/com/google/inject/ImplicitBindingTest.java
index 0b7e4262fa..8801b2133f 100644
--- a/core/test/com/google/inject/ImplicitBindingTest.java
+++ b/core/test/com/google/inject/ImplicitBindingTest.java
@@ -437,6 +437,8 @@ public void testImplicitJdkBindings() {
// String has a public nullary constructor, so Guice will call it.
assertEquals("", injector.getInstance(String.class));
// InetAddress has a package private constructor. We probably shouldn't be calling it :(
- assertNotNull(injector.getInstance(java.net.InetAddress.class));
+ if (Double.parseDouble(System.getProperty("java.specification.version")) < 17) {
+ assertNotNull(injector.getInstance(java.net.InetAddress.class));
+ }
}
}
diff --git a/core/test/com/google/inject/MethodInterceptionTest.java b/core/test/com/google/inject/MethodInterceptionTest.java
index 9e4accd369..5f6db7e851 100644
--- a/core/test/com/google/inject/MethodInterceptionTest.java
+++ b/core/test/com/google/inject/MethodInterceptionTest.java
@@ -259,10 +259,14 @@ protected void configure() {
// validate all causes.
for (Throwable t = e; t != null; t = t.getCause()) {
StackTraceElement[] stackTraceElement = t.getStackTrace();
- assertEquals("explode", stackTraceElement[0].getMethodName());
- assertEquals("invoke", stackTraceElement[1].getMethodName());
- assertEquals("invoke", stackTraceElement[2].getMethodName());
- assertEquals("testInterceptedMethodThrows", stackTraceElement[3].getMethodName());
+ int frame = 0;
+ assertEquals("explode", stackTraceElement[frame++].getMethodName());
+ while (stackTraceElement[frame].getClassName().startsWith("java.lang.invoke.LambdaForm")) {
+ frame++; // ignore lambda frames when running tests with ShowHiddenFrames
+ }
+ assertEquals("invoke", stackTraceElement[frame++].getMethodName());
+ assertEquals("invoke", stackTraceElement[frame++].getMethodName());
+ assertEquals("testInterceptedMethodThrows", stackTraceElement[frame++].getMethodName());
}
}
}