Jul 29, 2019

[java] lambda translation

Reference:
https://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html

Languages like C++, Java, which lately added support to lambda expression, internally compiler front-end generates mapping code for backend to parse and consume.

C++ generates lambda expression into type, with name mangling, provides most powerful way to capture/reference/move variables outside the lambda expression scope.

Java, OTHO, generates lambda expression to method, duh~


Stateless:
Like C++, Java generates lambda expression into static member function.
C++: http://vsdmars.blogspot.com/2016/07/c-non-capturing-c-lambdas-can-be.html
class A {
    public void foo() {
        List<string> list = ...
        list.forEach( s -> { System.out.println(s); } );
    }
}
to:
class A {
    public void foo() {
        List<string> list = ...
        list.forEach( [lambda for lambda$1 as Block] );
    }

    static void lambda$1(String s) {
        System.out.println(s);
    }
}


Capturing immutable values:
Unlike Go, which is smarter by replacing captured variable by value iff it's not referenced in later scope, Java does it by generate a static function with parameters copy by value.
class B {
    public void foo() {
        List<person> list = ...
        final int bottom = ..., top = ...;
        list.removeIf( p -> (p.size >= bottom && p.size <= top) );
    }
}
to:
class B {
    public void foo() {
        List<person> list = ...
        final int bottom = ..., top = ...;
        list.removeIf( [ lambda for lambda$1 as Predicate capturing (bottom, top) ]);
    }

    static boolean lambda$1(int bottom, int top, Person p) {
        return (p.size >= bottom && p.size <= top;
    }
}


The Lambda Metafactory:
Lambda capture will be implemented by an invokedynamic call site, whose static parameters describe the characteristics of the lambda body and lambda descriptor, and whose dynamic parameters (if any) are the captured values.

When invoked, this call site returns a lambda object for the corresponding lambda body and descriptor, bound to the captured values.

The bootstrap method for this callsite is a specified platform method called the lambda metafactory. (We can have a single metafactory for all lambda forms, or have specialized versions for common situations.)

The VM will call the metafactory only once per capture site; thereafter it will link the call site and get out of the way.

Call sites are linked lazily, so factory sites that are never invoked are never linked.

The static argument list for the basic metafactory looks like: 
metaFactory(MethodHandles.Lookup caller, // provided by VM
            String invokedName,          // provided by VM
            MethodType invokedType,      // provided by VM
            MethodHandle descriptor,     // lambda descriptor
            MethodHandle impl)           // lambda body

What about capturing type instance's value?
list.filter(e -> e.getSize() < minSize )
to:
list.forEach(INDY((MH(metaFactory), MH(invokeVirtual Predicate.apply),
                   MH(invokeVirtual B.lambda$1))( this ))));

private boolean lambda$1(Element e) {
    return e.getSize() < minSize;
}
EOF

As it shows, Java doesn't give you much control over instance's capture...

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.