diff --git a/core/src/com/google/inject/internal/ConstantFactory.java b/core/src/com/google/inject/internal/ConstantFactory.java index dc4c4aa552..dccfae40da 100644 --- a/core/src/com/google/inject/internal/ConstantFactory.java +++ b/core/src/com/google/inject/internal/ConstantFactory.java @@ -18,7 +18,9 @@ import com.google.common.base.MoreObjects; import com.google.inject.Provider; +import com.google.inject.internal.InjectorImpl.InjectorOptions; import com.google.inject.spi.Dependency; +import java.lang.invoke.MethodHandle; /** * @author crazybob@google.com (Bob Lee) @@ -65,6 +67,11 @@ public Provider makeProvider(InjectorImpl injector, Dependency dependency) return InternalFactory.makeProviderFor(instance, this); } + @Override + public MethodHandle getHandle(InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.constantFactoryGetHandle(dependency, instance); + } + @Override public String toString() { return MoreObjects.toStringHelper(ConstantFactory.class).add("value", instance).toString(); diff --git a/core/src/com/google/inject/internal/ExposedKeyFactory.java b/core/src/com/google/inject/internal/ExposedKeyFactory.java index 4e33f32ba4..b0d5f12a7d 100644 --- a/core/src/com/google/inject/internal/ExposedKeyFactory.java +++ b/core/src/com/google/inject/internal/ExposedKeyFactory.java @@ -17,8 +17,10 @@ package com.google.inject.internal; import com.google.inject.Key; +import com.google.inject.internal.InjectorImpl.InjectorOptions; import com.google.inject.spi.Dependency; import com.google.inject.spi.PrivateElements; +import java.lang.invoke.MethodHandle; /** * This factory exists in a parent injector. When invoked, it retrieves its value from a child @@ -63,4 +65,10 @@ public T get(InternalContext context, Dependency dependency, boolean linked) throw ipe.addSource(source); } } + + @Override + public MethodHandle getHandle(InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.catchInternalProvisionExceptionAndRethrowWithSource( + this.delegate.getHandle(options, dependency, linked), source); + } } diff --git a/core/src/com/google/inject/internal/FactoryProxy.java b/core/src/com/google/inject/internal/FactoryProxy.java index 293bab6327..98fadbf080 100644 --- a/core/src/com/google/inject/internal/FactoryProxy.java +++ b/core/src/com/google/inject/internal/FactoryProxy.java @@ -18,8 +18,10 @@ import com.google.common.base.MoreObjects; import com.google.inject.Key; +import com.google.inject.internal.InjectorImpl.InjectorOptions; import com.google.inject.internal.InjectorImpl.JitLimitation; import com.google.inject.spi.Dependency; +import java.lang.invoke.MethodHandle; /** * A placeholder which enables us to swap in the real factory once the injector is created. Used for @@ -62,6 +64,12 @@ public T get(InternalContext context, Dependency dependency, boolean linked) } } + @Override + public MethodHandle getHandle(InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.catchInternalProvisionExceptionAndRethrowWithSource( + targetFactory.getHandle(options, dependency, /* linked= */ true), targetKey); + } + @Override public String toString() { return MoreObjects.toStringHelper(FactoryProxy.class) diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java index 808d638a49..e30d898208 100644 --- a/core/src/com/google/inject/internal/InjectorImpl.java +++ b/core/src/com/google/inject/internal/InjectorImpl.java @@ -52,6 +52,7 @@ import com.google.inject.spi.TypeConverterBinding; import com.google.inject.util.Providers; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandle; import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; @@ -389,7 +390,7 @@ private static class SyntheticProviderBindingImpl extends BindingImpl InternalFactory> createInternalFactory(Binding providedBinding) { - // Defer calling `getProvider` until we need it, Bindingimple has an internal cache and by + // Defer calling `getProvider` until we need it, BindingImpl has an internal cache and by // delaying we can ensure we link to an optimized provider instance. return new InternalFactory>() { @Override @@ -401,6 +402,13 @@ public Provider get(InternalContext context, Dependency dependency, boolea public Provider> makeProvider(InjectorImpl injector, Dependency dependency) { return InternalFactory.makeProviderFor(providedBinding.getProvider(), this); } + + @Override + public MethodHandle getHandle( + InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.constantFactoryGetHandle( + Provider.class, providedBinding.getProvider()); + } }; } diff --git a/core/src/com/google/inject/internal/InjectorShell.java b/core/src/com/google/inject/internal/InjectorShell.java index 184bda7843..631a784615 100644 --- a/core/src/com/google/inject/internal/InjectorShell.java +++ b/core/src/com/google/inject/internal/InjectorShell.java @@ -41,6 +41,7 @@ import com.google.inject.spi.PrivateElements; import com.google.inject.spi.ProvisionListenerBinding; import com.google.inject.spi.TypeListenerBinding; +import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Optional; import java.util.logging.Logger; @@ -282,6 +283,12 @@ public Provider makeProvider(InjectorImpl injector, Dependency depe return InternalFactory.makeProviderFor(injector, this); } + @Override + public MethodHandle getHandle( + InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.constantFactoryGetHandle(dependency, injector); + } + @Override public String toString() { return "Provider"; @@ -325,6 +332,12 @@ public Provider makeProvider(InjectorImpl injector, Dependency depend return InternalFactory.makeProviderFor(makeLogger(dependency), this); } + @Override + public MethodHandle getHandle( + InjectorOptions options, Dependency dependency, boolean linked) { + return InternalMethodHandles.constantFactoryGetHandle(dependency, makeLogger(dependency)); + } + private static Logger makeLogger(Dependency dependency) { InjectionPoint injectionPoint = dependency.getInjectionPoint(); return injectionPoint == null diff --git a/core/src/com/google/inject/internal/InternalMethodHandles.java b/core/src/com/google/inject/internal/InternalMethodHandles.java index 61e6a1f97d..eff7e65829 100644 --- a/core/src/com/google/inject/internal/InternalMethodHandles.java +++ b/core/src/com/google/inject/internal/InternalMethodHandles.java @@ -88,6 +88,52 @@ static MethodHandle findConstructorOrDie(Class clazz, MethodType type) { } } + /** + * Returns a method handle with the signature {@code (InternalContext) -> T} that returns the + * given instance. + */ + static MethodHandle constantFactoryGetHandle(Dependency dependency, Object instance) { + return constantFactoryGetHandle(dependency.getKey().getTypeLiteral().getRawType(), instance); + } + + /** + * Returns a method handle with the signature {@code (InternalContext) -> T} that returns the + * given instance. + */ + static MethodHandle constantFactoryGetHandle(Class forType, Object instance) { + var constant = MethodHandles.constant(forType, instance); + return MethodHandles.dropArguments(constant, 0, InternalContext.class); + } + + private static final MethodHandle INTERNAL_PROVISION_EXCEPTION_ADD_SOURCE_HANDLE = + findVirtualOrDie( + InternalProvisionException.class, + "addSource", + methodType(InternalProvisionException.class, Object.class)); + + /** + * Surrounds the delegate with a catch block that rethrows the exception with the given source. + * + *
{@code
+   * try {
+   *   return delegate(...);
+   * } catch (InternalProvisionException ipe) {
+   *   throw ipe.addSource(source);
+   * }
+   * }
+ */ + static MethodHandle catchInternalProvisionExceptionAndRethrowWithSource( + MethodHandle delegate, Object source) { + var addSourceAndRethrow = + MethodHandles.filterReturnValue( + MethodHandles.insertArguments( + InternalMethodHandles.INTERNAL_PROVISION_EXCEPTION_ADD_SOURCE_HANDLE, 1, source), + MethodHandles.throwException( + delegate.type().returnType(), InternalProvisionException.class)); + return MethodHandles.catchException( + delegate, InternalProvisionException.class, addSourceAndRethrow); + } + /** * Generates a provider instance that delegates to the given factory. *