From 4bd504c2c0c1a10a2a518cb62e26d6e1b55aad29 Mon Sep 17 00:00:00 2001 From: Rebeca Gallardo Date: Tue, 3 Oct 2023 19:17:38 -0700 Subject: [PATCH] Fallback to reflection if building lambda instance fails. In some Java versions (9 and 11 at least, others not tested) the call to LambdaMetafactory fails with a mysterious "invalid bytecode" error. Instead of trying to hack our way around the quirks of an already-deprecated java version, we simply fall back to calling the method via reflection. It's slower but it works. Closes #682 --- .github/workflows/nebula-ci.yml | 4 +- .../netflix/archaius/ConfigProxyFactory.java | 53 +++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/.github/workflows/nebula-ci.yml b/.github/workflows/nebula-ci.yml index d457c2de..e80c9d07 100644 --- a/.github/workflows/nebula-ci.yml +++ b/.github/workflows/nebula-ci.yml @@ -10,8 +10,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # test against JDK 8, 17 and 21. - java: [ 8, 17, 21] + # test against may JDKs. Only 8, 17 and 21 are officially supported, fixes for others are on a best effort basis. + java: [ 8, 9, 11, 17, 21] name: CI with Java ${{ matrix.java }} steps: - uses: actions/checkout@v3 diff --git a/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java b/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java index 3558eba6..6ec6bad6 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java @@ -17,7 +17,6 @@ import javax.inject.Inject; import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaConversionException; import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -451,14 +450,21 @@ private static Function asFunction(MethodHandles.Lookup lookup, methodHandle, methodHandle.type()); return (Function) site.getTarget().invokeExact(); + } catch (VerifyError ve) { + // This happens in java 9 and 11 (maybe others, we haven't checked. 8 and 17 onwards are known good) + // The generated bytecode for the CallSite has bad bytecode and can't be loaded as a class. + // For this case, we'll just fall back to calling the method handle. It's slower but works. + return o -> { + try { + return methodHandle.invoke(o); + } catch (Throwable t) { + maybeWrapThenRethrow(t); + return null; // Unreachable, but the compiler can't know + } + }; } catch (Throwable t) { - if (t instanceof RuntimeException) { - throw (RuntimeException) t; - } - if (t instanceof Error) { - throw (Error) t; - } - throw new RuntimeException(t); + maybeWrapThenRethrow(t); + return null; // Unreachable, but the compiler can't know } } @@ -476,14 +482,31 @@ private static BiFunction asBiFunction(MethodHandles.Loo methodHandle, methodHandle.type()); return (BiFunction) site.getTarget().invokeExact(); - } catch (Throwable t) { - if (t instanceof RuntimeException) { - throw (RuntimeException) t; - } - if (t instanceof Error) { - throw (Error) t; + } catch (VerifyError ve) { + // This happens in java 9 and 11 (maybe others, we haven't checked. 8 and 17 onwards are known good) + // The generated bytecode for the CallSite has bad bytecode and can't be loaded as a class. + // For this case, we'll just fall back to calling the method handle. It's slower but works. + return (o1, o2) -> { + try { + return methodHandle.invoke(o1, o2); + } catch (Throwable t) { + maybeWrapThenRethrow(t); + return null; // Unreachable, but the compiler can't know } - throw new RuntimeException(t); + }; + } catch (Throwable t) { + maybeWrapThenRethrow(t); + return null; // Unreachable, but the compiler can't know + } + } + + private static void maybeWrapThenRethrow(Throwable t) { + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + if (t instanceof Error) { + throw (Error) t; } + throw new RuntimeException(t); } }