Giter VIP home page Giter VIP logo

lmbda's Introduction

Lmbda Discord Maven Central

This is library that can be used to generate lambdas from method handles with any kind of access modifier without any performance loss. This includes methods, constructors, field accessors (getter, setter) and any other MethodHandle that can be constructed.

How to use

Every available MethodHandle can be implemented by a functional interface. But only if the two method signatures match. Object types will be auto casted to the target type if possible, the same goes for boxing/unboxing of primitive types.

Non-static methods will always take an extra parameter which represents the target object of the method. This is always the first parameter.

Note: Generating functions requires that you have access to the target class. Since Java 9, this is even more important because the module your code is located in must be able to access the module of the target class, see MethodHandles#privateLookupIn for more info.

Generating method functions

Static setter method

Consider the following class which holds a static setter function.

class MyObject {
    private static void set(int value) {
        // ...
    }
}

If you want to generate a function for the static set(int) method, you need to use a interface which takes one parameter (value) and return void to reflect the method signature. The IntConsumer is a good choice available within the Java 8 API.

final MethodHandles.Lookup lookup = MethodHandlesExtensions.privateLookupIn(MyObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findStatic(MyObject.class, "set", MethodType.methodType(void.class, int.class));

final IntConsumer setter = LambdaFactory.create(LambdaType.of(IntConsumer.class), methodHandle);
setter.accept(1000);

Setter method

Consider the following class which holds a setter function.

class MyObject {
    private void set(int value) {
        // ...
    }
}

If you want to generate a function for the static set(int) method, you need to use a interface which takes two parameters:

  1. The target of where the method is declared, this will be the target object on which the method is invoked. E.g. target.set(value)
  2. The value that will be passed to the set method.

the first one is the target where the method is declared, MyObject (value) and return void to reflect the method signature. The ObjIntConsumer is a good choice available within the Java 8 API.

final MethodHandles.Lookup lookup = MethodHandlesExtensions.privateLookupIn(MyObject.class, MethodHandles.lookup());
final MethodHandle methodHandle = lookup.findVirtual(MyObject.class, "set", MethodType.methodType(void.class, int.class));

final ObjIntConsumer<MyObject> setter = LambdaFactory.create(new LambdaType<ObjIntConsumer<MyObject>>() {}, methodHandle);

final MyObject myObject = new MyObject();
setter.accept(myObject, 1000);

Benchmarks

Below you can find a few benchmarks. lmbda will be generated at runtime, all the other ones are implemented manually. The generated structure of the lmbda is almost the same as the static_mh.

The following benchmark implements a lambda function to access a int field, a ToIntFunction is implemented in this case to avoid boxing/unboxing.

The benchmark was run on an AMD Ryzen 9 5900X CPU and using JDK 16.0.2

Benchmark Mode Cnt Score Error Units
IntGetterFieldBenchmark.direct avgt 15 1.304 ± 0.008 ns/op
IntGetterFieldBenchmark.fieldConst avgt 15 2.698 ± 0.061 ns/op
IntGetterFieldBenchmark.fieldDyn avgt 15 2.986 ± 0.005 ns/op
IntGetterFieldBenchmark.lmbda avgt 15 1.299 ± 0.005 ns/op
IntGetterFieldBenchmark.methodHandleConst avgt 15 1.300 ± 0.004 ns/op
IntGetterFieldBenchmark.methodHandleDyn avgt 15 3.013 ± 0.018 ns/op
IntGetterFieldBenchmark.methodHandleProxy avgt 15 7.530 ± 0.185 ns/op
IntGetterFieldBenchmark.plain avgt 15 1.304 ± 0.006 ns/op

This benchmark returns a Integer through the getValue method from a target object.

Benchmark Mode Cnt Score Error Units
IntGetterMethodBenchmark.direct avgt 15 1.372 ± 0.003 ns/op
IntGetterMethodBenchmark.lambdaMetafactory avgt 15 1.387 ± 0.024 ns/op
IntGetterMethodBenchmark.lmbda avgt 15 1.370 ± 0.004 ns/op
IntGetterMethodBenchmark.methodConst avgt 15 3.731 ± 0.106 ns/op
IntGetterMethodBenchmark.methodDyn avgt 15 4.164 ± 0.139 ns/op
IntGetterMethodBenchmark.methodHandleConst avgt 15 1.612 ± 0.379 ns/op
IntGetterMethodBenchmark.methodHandleDyn avgt 15 3.852 ± 0.116 ns/op
IntGetterMethodBenchmark.methodHandleProxy avgt 15 8.085 ± 0.070 ns/op
IntGetterMethodBenchmark.plain avgt 15 1.398 ± 0.025 ns/op

lmbda's People

Contributors

cybermaxke avatar jackpgreen avatar leosarra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

lmbda's Issues

`ObjLongConsumer` throws `WrongMethodTypeException`

When trying to create a LambdaType of ObjLongConsumer, a WrongMethodTypeException is thrown.
An equivalent ObjIntConsumer can be created without issue.

Take the following example:

import org.lanternpowered.lmbda.*;
import java.lang.invoke.*;
import java.util.function.*;

public class LambdaFactoryLongException {
    public int intField;
    public long longField;

    public static void main(String[] args) throws ReflectiveOperationException {
        MethodHandle intFieldMethodHandle = MethodHandles.lookup()
                .unreflectSetter(LambdaFactoryLongException.class.getDeclaredField("intField"));
        MethodHandle longFieldMethodHandle = MethodHandles.lookup()
                .unreflectSetter(LambdaFactoryLongException.class.getDeclaredField("longField"));

        // Works
        LambdaFactory.create(new LambdaType<ObjIntConsumer<Object>>() {
        }, intFieldMethodHandle);

        LambdaFactory.create(new LambdaType<ObjIntConsumer<Object>>() {
        }, longFieldMethodHandle);

        LambdaFactory.create(new LambdaType<BiConsumer<Object, Long>>() {
        }, longFieldMethodHandle);

        // Breaks
        LambdaFactory.create(new LambdaType<ObjLongConsumer<Object>>() {
        }, longFieldMethodHandle);
    }
}

The following exception is thrown:

Exception in thread "main" java.lang.IllegalStateException: Couldn't create lambda for: "MethodHandle(DTO,long)void". Failed to implement: LambdaType[type=java.util.function.ObjLongConsumer<java.lang.Object>,method=accept(Object,long)void]
	at org.lanternpowered.lmbda.InternalLambdaFactory.create(InternalLambdaFactory.java:143)
	at org.lanternpowered.lmbda.LambdaFactory.create(LambdaFactory.java:808)
	at com.me.LambdaFactoryLongException.main(LambdaFactoryLongException.java:30)
Caused by: java.lang.ClassFormatError: Arguments can't fit into locals in class file org/lanternpowered/lmbda/Lmbda$4
	at java.base/java.lang.ClassLoader.defineClass0(Native Method)
	at java.base/java.lang.System$2.defineClass(System.java:2307)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2439)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClassAsLookup(MethodHandles.java:2420)
	at java.base/java.lang.invoke.MethodHandles$Lookup.defineHiddenClass(MethodHandles.java:2127)
	at org.lanternpowered.lmbda.InternalLambdaFactory.lambda$createGeneratedFunction$1(InternalLambdaFactory.java:354)
	at org.lanternpowered.lmbda.InternalUtilities.doUnchecked(InternalUtilities.java:79)
	at org.lanternpowered.lmbda.InternalLambdaFactory.createGeneratedFunction(InternalLambdaFactory.java:353)
	at org.lanternpowered.lmbda.InternalLambdaFactory.create(InternalLambdaFactory.java:141)
	... 2 more

From this line:
LambdaFactory.create(new LambdaType<ObjLongConsumer<Object>>() {}, longFieldMethodHandle);

Reproduced in d1ffaab

Declare optional modular dependencies as static

The build.gradle notes the dependencies on Guava and the Kotlin standard library are optional.

However, the module descriptor does not use the static modifier, which makes them mandatory:

open module org.lanternpowered.lmbda {
    requires org.objectweb.asm;
    requires kotlin.stdlib;
    requires com.google.common;

    exports org.lanternpowered.lmbda;
    exports org.lanternpowered.lmbda.kt;
}

As a somewhat related question, why is the module made open? Does one of Lmbda's dependencies reflect into the main code? I do not think ASM, Guava, or Kotlin would do that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.