[code-reflection] RFR: Add isQuotable attribute to LambdaOp [v6]

Maurizio Cimadamore mcimadamore at openjdk.org
Wed Sep 10 11:55:32 UTC 2025


On Wed, 10 Sep 2025 11:26:36 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

> So, more generally, I think both `Interpreter` and `BytecodeGenerator` might have some issues in this area.

I tried something like this:


class Test {
    public interface A<X> {
        void m(X x);
    }

    public interface B<X extends Number> {
        void m(X x);
    }

    public interface C extends A<Integer>, B<Integer> { }

    @CodeReflection
    static C getC() {
        C c = i -> System.out.println(i);
        return c;
    }

    public static void main(String[] args) throws Throwable {
        var method = Test.class.getDeclaredMethod("getC");
        var op = (CoreOp.FuncOp) Op.ofMethod(method).get();
        A<?> a = (A<?>)Interpreter.invoke(MethodHandles.lookup(), op);
        a.m(null);
    }
}


This (surprisingly) seems to work. I believe the reason is that `Interpreter` uses `MethodHandleProxies::asInterfaceInstance` -- whose javadoc says:


* Because of the possibility of {@linkplain java.lang.reflect.Method#isBridge bridge methods}
     * and other corner cases, the interface may also have several abstract methods
     * with the same name but having distinct descriptors (types of returns and parameters).
     * In this case, all the methods are bound in common to the one given target.
     * The type check and effective {@code asType} conversion is applied to each
     * method type descriptor, and all abstract methods are bound to the target in common.
     * Beyond this type check, no further checks are made to determine that the
     * abstract methods are related in any way.


So, this special "fallback overrides" end up saving the day in this case.

However, a similar example using `BytecodeGenerator` fails:


class Test {
    public interface A<X> {
        void m(X x);
    }

    public interface B<X extends Number> {
        void m(X x);
    }

    public interface C extends A<Integer>, B<Integer> { }

    @CodeReflection
    static void run() {
        C c = i -> System.out.println(i);
        A<?> a = (A<?>)c;
        a.m(null);
    }

    public static void main(String[] args) throws Throwable {
        var method = Test.class.getDeclaredMethod("run");
        var op = (CoreOp.FuncOp) Op.ofMethod(method).get();
        var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
        handle.invokeExact();
    }
}


The error I see is:


WARNING: Using incubator modules: jdk.incubator.code
Exception in thread "main" java.lang.AbstractMethodError: Receiver class run_0x0000000097069000$$Lambda/0x000000009706dbb0 does not define or inherit an implementation of the resolved method 'abstract void m(java.lang.Object)' of interface Test$A.
	at Test.main(Test.java:33)


Which is kind of expected: the generated instance is missing a bridge (as it's generated via regular metafactory).

-------------

PR Comment: https://git.openjdk.org/babylon/pull/545#issuecomment-3274613153


More information about the babylon-dev mailing list