SlideShare a Scribd company logo
InvokeBinder
Fluent Programming for MethodHandles
InvokeBinder
The Missing java.lang.invoke API
Me
• "Charles Oliver Nutter" <headius@headius.com>
• @headius
• Red Hat, JBoss, “Research and Prototyping” (nee Polyglot)
MethodHandle
Function and field pointers
Argument and return value
manipulation
Flow control and exception
handling
And the JVM optimizes it away
(Eventually…sometimes…)
So it’s all good, right?
MethodHandle nativeTarget = MethodHandles.findVirtual(targetClass, targetName, targetType);
// handle return value

if (nativeReturn == long.class) {

MethodHandle returnFilter = MethodHandles.insertArguments(

MethodHandles.findStatic(RubyFixnum.class,
“newFixnum",
MethodType.methodType(RubyFixnum.class, Ruby.class, long.class)),

0,

runtime);
nativeTarget = MethodHandles.filterReturnValue(nativeTarget,
returnFilter);
Problem #1:
Verbosity
MethodHandle nativeTarget = findVirtual(targetClass, targetName, targetType);
// handle return value

if (nativeReturn == long.class) {

// native integral type, produce a Fixnum

MethodHandle returnFilter = insertArguments(

findStatic(RubyFixnum.class,
“newFixnum",
methodType(RubyFixnum.class, Ruby.class, long.class)),

0,

runtime);
nativeTarget = filterReturnValue(nativeTarget,
returnFilter);
...
• insert
• drop
• cast
• invoke Have to start at the target and work back
Problem #2:
Composition in reverse
MethodHandle nativeTarget = findVirtual(targetClass, targetName, targetType);
// handle return value

if (nativeReturn == long.class

|| nativeReturn == short.class

|| nativeReturn == char.class

|| nativeReturn == int.class

|| nativeReturn == long.class) {

// native integral type, produce a Fixnum

nativeTarget = explicitCastArguments(nativeTarget,
methodType(long.class, targetArgs));

MethodHandle returnFilter = insertArguments(

findStatic(RubyFixnum.class,
“newFixnum",
methodType(RubyFixnum.class, Ruby.class, long.class)),

0,

runtime);
nativeTarget = filterReturnValue(nativeTarget,
returnFilter);
...
Problem #3:
MethodType wrangling
public Object tryFinally(MethodHandle target, MethodHandle post) throws Throwable {

try {

return target.invoke();

} finally {

post.invoke();

}

}
ETOOMUCHCODE
try/finally
1. Like javac, finally path must be duplicated
2. Normal path invokes post, returns result of body
3. Exceptional path drops return, invokes post, re-raises exception
4. Now do this entirely on call stack, with no temp vars
public Object tryFinally(MethodHandle target, MethodHandle post) throws Throwable {

try {

return target.invoke();

} finally {

post.invoke();

}

}
MethodHandle exceptionHandler = Binder

.from(target.type().insertParameterTypes(0, Throwable.class).changeReturnType(void.class))

.drop(0)

.invoke(post);



MethodHandle rethrow = Binder

.from(target.type().insertParameterTypes(0, Throwable.class))

.fold(exceptionHandler)

.drop(1, target.type().parameterCount())

.throwException();



target = MethodHandles.catchException(target, Throwable.class, rethrow);



// if target returns a value, we must return it regardless of post

MethodHandle realPost = post;

if (target.type().returnType() != void.class) {

// modify post to ignore return value

MethodHandle newPost = Binder

.from(target.type().insertParameterTypes(0, target.type().returnType()).changeReturnType(void.class))

.drop(0)

.invoke(post);



// fold post into an identity chain that only returns the value

realPost = Binder

.from(target.type().insertParameterTypes(0, target.type().returnType()))

.fold(newPost)

.drop(1, target.type().parameterCount())

.identity();

}



return MethodHandles.foldArguments(realPost, target);
Problem #4:
Complicated forms
The Problems
• Verbosity
• Composition in reverse
• MethodType must match exactly all the way through
• Many common patterns are complicated to represent
• Argument manipulation is by absolute offsets
• Varargs and argument collection often need fixed size
• Transforms can’t be easily shared across signatures
The Solution
InvokeBinder
https://github.com/headius/invokebinder
com.headius:invokebinder
com.headius.invokebinder
Binder
• Binder.from(type) produces a new Binder (which is immutable)
• Binder#drop, insert, permute, etc act in call-forward direction
• Binder#fold, filter, catchException, etc take additional handles
• Endpoints: invoke*, set/getField, arraySet/Get, constant, identity,
nop, branch (guardWithTest)
• Utilities for larger constructs
String value1 = System.getProperty("foo");
MethodHandle m1 = lookup
        .findStatic(System.class, "getProperty",
MethodType.methodType(String.class, String.class));
MethodHandle m2 = Binder.from(String.class, String.class)
        .invokeStatic(lookup, System.class, "getProperty");
Static Method
Lookup can also be passed into Binder#withLookup, eliminating this param
PrintStream out1 = System.out;
MethodHandle m3 = lookup
        .findStaticGetter(System.class, "out", PrintStream.class);
MethodHandle m4 = Binder.from(PrintStream.class)
        .getStatic(lookup, System.class, "out");
Static Field Get
class MyStruct {
    public String name;
}
MyStruct ms = new MyStruct();
MethodHandle m5 = lookup
        .findSetter(MyStruct.class, "name", String.class);
MethodHandle m6 = Binder.from(void.class, MyStruct.class, String.class)
        .setField(lookup, "name");
Instance Field Set
No target type; Binder knows it already!
MethodHandle m9 = lookup
        .findStatic(Demo1.class, "twoArgs",
                MethodType.methodType(String.class, String.class, String.class));
m9 = MethodHandles.dropArguments(m9, 2, String.class);
MethodHandle m10 = Binder.from(String.class, String.class, String.class,
String.class)
        .drop(2)
        .invokeStatic(lookup, Demo1.class, "twoArgs");
m10.invoke("one", "two", "three"); // => "[one,two]"
Deleting Args
MethodHandle m11 = lookup
        .findStatic(Demo1.class, "twoArgs",
                MethodType.methodType(String.class, String.class, String.class));
m11 = MethodHandles.permuteArguments(
       m11,
        MethodType.methodType(String.class, String.class, String.class, int.class),
        1, 0);
MethodHandle m12 = Binder.from(String.class, String.class, String.class, int.class)
       .permute(1, 0)
        .invokeStatic(lookup, Demo1.class, "initials");
m12.invoke("one", "two", 3); // => "[two,one]"
Permute Args
MethodHandle m13 = lookup
        .findStatic(Demo1.class, "threeArgs",
                MethodType.methodType(String.class, String.class,
String.class, String.class));
MethodHandle combiner = lookup
        .findStatic(Demo1.class, "initials",
                MethodType.methodType(String.class, String.class,
String.class));
m13 = MethodHandles.foldArguments(m13, combiner);
MethodHandle m14 = Binder.from(String.class, String.class, String.class)
        .fold(
                Binder
                        .from(String.class, String.class, String.class)
                        .invokeStatic(lookup, Demo1.class, "initials")
        )
        .invokeStatic(lookup, Demo1.class, "threeArgs");
m14.invoke("Charles", "Nutter"); // => ["CN", "Charles", "Nutter"]
Fold
MethodHandle m15 = lookup
        .findStatic(Demo1.class, "twoArgs",
                MethodType.methodType(String.class, String.class,
String.class));
MethodHandle filter = lookup
        .findStatic(Demo1.class, "upcase",
                MethodType.methodType(String.class, String.class));
m15 = MethodHandles.filterArguments(m15, 0, filter, filter);
MethodHandle m16 = Binder.from(String.class, String.class, String.class)
        .filter(0,
                 Binder.from(String.class, String.class)
                        .invokeStatic(lookup, Demo1.class, "upcase")
        )
        .invokeStatic(lookup, Demo1.class, "twoArgs");
m16.invoke("hello", "world"); // => ["HELLO", "WORLD"]
Filter
MethodHandle m21 = Binder.from(String.class, int.class, String.class)
        .branch(
                Binder.from(boolean.class, int.class, String.class)
                        .drop(1)
                        .invokeStatic(lookup, Demo1.class, "upOrDown"),
                Binder.from(String.class, int.class, String.class)
                        .drop(0)
                        .invokeStatic(lookup, Demo1.class, "upcase"),
                Binder.from(String.class, int.class, String.class)
                        .drop(0)
                        .invokeStatic(lookup, Demo1.class, "downcase")
        );
m21.invoke(1, "MyString"); // => "MYSTRING"
m21.invoke(0, "MyString"); // => "mystring"
Boolean Branch
Debugging!
handle = Binder

.from(String.class, Object.class, double.class, String.class)

.dropFirst(2)

.printType()

.insert(1, "world")

.invoke(target);
No-op, just prints out current type
MethodHandle handle = Binder

.from(void.class, String[].class)

.tryFinally(post)

.invokeStatic(LOOKUP, BinderTest.class, "setZeroToFoo");
• Binder aggregates MethodType, list of transforms
• While building
• MethodType is transformed (and verified or cast)
• Transform stack pushes down a transform
• At endpoint transform stack is played in reverse
• And verified or cast (perhaps redundant)
/**

* Filter incoming arguments, starting at the given index, replacing
* each with the result of calling the associated function in the
* given list.

*

* @param index the index of the first argument to filter

* @param functions the array of functions to transform the arguments

* @return a new Binder

*/

public Binder filter(int index, MethodHandle... functions) {

return new Binder(this, new Filter(index, functions));

}
public class Filter extends Transform {



private final int index;

private final MethodHandle[] functions;



public Filter(int index, MethodHandle... functions) {

this.index = index;

this.functions = functions;

}



public MethodHandle up(MethodHandle target) {

return MethodHandles.filterArguments(target, index, functions);

}



public MethodType down(MethodType type) {

for (int i = 0; i < functions.length; i++) {

type = type.changeParameterType(index + i, functions[i].type().returnType());

}

return type;

}



public String toString() {

return "fold args from " + index + " with " + Arrays.toString(functions);

}

}
BUT WAIT THERE’S MORE!
Argument juggling sucks
MethodHandle handle2 = Binder

.from(Subjects.StringIntegerIntegerIntegerString.type())

.collect(1, 3, Integer[].class)

.invoke(Subjects.StringIntegersStringHandle);
If I need to change signature, all my offsets break
Need a representation of an
argument list…not just types
Signature
• Combines MethodType with argument names
• Perform argument manipulations by name, not offset
• Regex-based operations: collect, drop, permute
• Conversion: signature1.to(signature2) => permute offsets
• Type checking, pretty toString, other niceties
Signature sig = Signature

.returning(String.class)

.asFold(Object.class);


assertEquals(Object.class, sig.type().returnType());
Signature sig = Signature

.returning(String.class)

.appendArg("obj", Object.class)

.appendArg("num", int.class)

.insertArg("num", "flo", float.class);



assertEquals("(Object obj, float flo, int num)String", sig.toString());
private static final Signature stringObjectInt = Signature

.returning(String.class)

.appendArg("obj", Object.class)

.appendArg("num", int.class);
Signature sig = stringObjectInt

.appendArg("flo", float.class)

.appendArg("dub", double.class)

.permute("obj", "dub");



assertEquals("(Object obj, double dub)String", sig.toString());

int[] permuteInts = stringObjectInt

.appendArg("flo", float.class)

.appendArg("dub", double.class)

.to(stringObjectInt);



assertArrayEquals(new int[] {0, 1}, permuteInts);
int[] permuteInts = stringObjectInt

.appendArg("flo", float.class)

.appendArg("dub", double.class)

.to(".*o.*");



assertArrayEquals(new int[] {0, 2}, permuteInts);



permuteInts = stringObjectInt

.appendArg("flo", float.class)

.appendArg("dub", double.class)

.to("num", "dub");



assertArrayEquals(new int[] {1, 3}, permuteInts);
Signature sig = stringObjectInt

.appendArg("flo", float.class)

.appendArg("dub", double.class)

.exclude("obj", "dub");



assertEquals("(int num, float flo)String", sig.toString());
public static final Signature StringIntegerIntegerIntegerString = Signature

.returning(String.class)

.appendArg("a", String.class)

.appendArg("b1", Integer.class)

.appendArg("b2", Integer.class)

.appendArg("b3", Integer.class)

.appendArg("c", String.class);
Signature oldSig = Subjects.StringIntegerIntegerIntegerString;

Signature newSig = oldSig.collect("bs", "b.*");



assertEquals(Integer[].class, newSig.argType(1));

assertEquals("bs", newSig.argName(1));

assertEquals(3, newSig.argCount());

assertEquals("c", newSig.argName(2));
SmartBinderBinder Signature
SmartBinder
• All operations from Binder, but Signature-aware
• Variable-length argument lists
• Regex-based transforms
• Result is a SmartHandle (MethodHandle + Signature)
MethodHandle StringIntegersStringHandle = Binder

.from(String.class, String.class, Integer[].class, String.class)

.invokeStaticQuiet(LOOKUP, Subjects.class, "stringIntegersString");
MethodHandle StringIntegersStringHandle = Binder

.from(String.class, String.class, Integer[].class, String.class)

.invokeStaticQuiet(LOOKUP, Subjects.class, "stringIntegersString");
Signature oldSig = Subjects.StringIntegerIntegerIntegerString;



SmartHandle handle = SmartBinder

.from(oldSig)

.collect("bs", "b.*")

.invoke(Subjects.StringIntegersStringHandle);
Signature oldSig = Subjects.StringIntegerIntegerIntegerString;



SmartHandle handle = SmartBinder.from(oldSig)

.drop("b1")

.drop("b2")

.drop("b3")

.insert(1, "bs", new Integer[]{1, 2, 3})

.invoke(Subjects.StringIntegersStringHandle);
MethodHandle target = Subjects.concatHandle();

MethodHandle filter = MethodHandles.insertArguments(Subjects.concatHandle(), 1, "goodbye");

MethodHandle handle = SmartBinder

.from(String.class, arrayOf(“arg1", “arg2”), String.class, String.class)

.filter("arg.*", filter)

.invoke(target).handle();
Signature.returning(IRubyObject.class)

.appendArg("context", ThreadContext.class)

.appendArg("caller", IRubyObject.class)

.appendArg("self", IRubyObject.class)

.appendArg("arg0", IRubyObject.class)

.appendArg("arg1", IRubyObject.class)

.appendArg("arg2", IRubyObject.class)
SmartBinder.from(site.signature)

.permute("context", "arg.*")
MethodHandle stringInt = Binder

.from(String.class, int.class)

.invokeStaticQuiet(LOOKUP, Integer.class, “toString");
MethodHandle handle = SmartBinder

.from(String.class, "i", int.class)

.fold("s", stringInt)

.dropLast()

.identity()

.handle();



assertEquals(MethodType.methodType(String.class, int.class), handle.type());

assertEquals("15", (String)handle.invokeExact(15));
Status
• Binder supports nearly all j.l.i.MethodHandle(s) operations
• Signature has a wide array of transforms
• SmartBinder has what I needed
TODO
• Analyze transform chain to emit more efficient handles
• Fill out missing functionality, tests, docs
• More utility forms
• Generate bytecode or .java callable for non-indy
• “Interpreted” [Smart]Binder#call like LFs for non-bytecode-loading
• More extensive debugging (current transform stack, visual graph)
Thank you!
• "Charles Oliver Nutter" <headius@headius.com>
• @headius
• https://github.com/headius/invokebinder
• Maven: com.headius:invokebinder

More Related Content

What's hot

Let's refine your Scala Code
Let's refine your Scala CodeLet's refine your Scala Code
Let's refine your Scala Code
Tech Triveni
 
Introduction to Client-Side Javascript
Introduction to Client-Side JavascriptIntroduction to Client-Side Javascript
Introduction to Client-Side Javascript
Julie Iskander
 
7 rules of simple and maintainable code
7 rules of simple and maintainable code7 rules of simple and maintainable code
7 rules of simple and maintainable code
Geshan Manandhar
 
Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
Pavel Albitsky
 
Java script questions
Java script questionsJava script questions
Java script questionsSrikanth
 
Object oriented programming with python
Object oriented programming with pythonObject oriented programming with python
Object oriented programming with python
Arslan Arshad
 
Clean code
Clean codeClean code
Clean code
Henrique Smoco
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
Mario Sangiorgio
 
Clean code
Clean codeClean code
Clean code
ifnu bima
 
Solid principles
Solid principlesSolid principles
Solid principles
Hanokh Aloni
 
Java fundamentals
Java fundamentalsJava fundamentals
Java fundamentalsHCMUTE
 
Clean Code: Chapter 3 Function
Clean Code: Chapter 3 FunctionClean Code: Chapter 3 Function
Clean Code: Chapter 3 Function
Kent Huang
 
Recommending Method Invocation Context Changes
Recommending Method Invocation Context ChangesRecommending Method Invocation Context Changes
Recommending Method Invocation Context ChangesBeat Fluri
 
Object oriented programming in python
Object oriented programming in pythonObject oriented programming in python
Object oriented programming in python
baabtra.com - No. 1 supplier of quality freshers
 
Swift for TensorFlow - CoreML Personalization
Swift for TensorFlow - CoreML PersonalizationSwift for TensorFlow - CoreML Personalization
Swift for TensorFlow - CoreML Personalization
Jacopo Mangiavacchi
 
Unit2wt
Unit2wtUnit2wt
Unit2wt
vamsi krishna
 
php&mysql with Ethical Hacking
php&mysql with Ethical Hackingphp&mysql with Ethical Hacking
php&mysql with Ethical Hacking
BCET
 
Joose @jsconf
Joose @jsconfJoose @jsconf
Joose @jsconfmalteubl
 
Javascript
JavascriptJavascript
Javascript
Manav Prasad
 
Groovy 2.0 webinar
Groovy 2.0 webinarGroovy 2.0 webinar
Groovy 2.0 webinar
Guillaume Laforge
 

What's hot (20)

Let's refine your Scala Code
Let's refine your Scala CodeLet's refine your Scala Code
Let's refine your Scala Code
 
Introduction to Client-Side Javascript
Introduction to Client-Side JavascriptIntroduction to Client-Side Javascript
Introduction to Client-Side Javascript
 
7 rules of simple and maintainable code
7 rules of simple and maintainable code7 rules of simple and maintainable code
7 rules of simple and maintainable code
 
Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
 
Java script questions
Java script questionsJava script questions
Java script questions
 
Object oriented programming with python
Object oriented programming with pythonObject oriented programming with python
Object oriented programming with python
 
Clean code
Clean codeClean code
Clean code
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
 
Clean code
Clean codeClean code
Clean code
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Java fundamentals
Java fundamentalsJava fundamentals
Java fundamentals
 
Clean Code: Chapter 3 Function
Clean Code: Chapter 3 FunctionClean Code: Chapter 3 Function
Clean Code: Chapter 3 Function
 
Recommending Method Invocation Context Changes
Recommending Method Invocation Context ChangesRecommending Method Invocation Context Changes
Recommending Method Invocation Context Changes
 
Object oriented programming in python
Object oriented programming in pythonObject oriented programming in python
Object oriented programming in python
 
Swift for TensorFlow - CoreML Personalization
Swift for TensorFlow - CoreML PersonalizationSwift for TensorFlow - CoreML Personalization
Swift for TensorFlow - CoreML Personalization
 
Unit2wt
Unit2wtUnit2wt
Unit2wt
 
php&mysql with Ethical Hacking
php&mysql with Ethical Hackingphp&mysql with Ethical Hacking
php&mysql with Ethical Hacking
 
Joose @jsconf
Joose @jsconfJoose @jsconf
Joose @jsconf
 
Javascript
JavascriptJavascript
Javascript
 
Groovy 2.0 webinar
Groovy 2.0 webinarGroovy 2.0 webinar
Groovy 2.0 webinar
 

Similar to InvokeBinder: Fluent Programming for Method Handles

Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scalashinolajla
 
Developer testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticDeveloper testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing Fanatic
LB Denker
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
Mite Mitreski
 
JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015
Charles Nutter
 
Session 08 - Arrays and Methods
Session 08 - Arrays and MethodsSession 08 - Arrays and Methods
Session 08 - Arrays and Methods
SiddharthSelenium
 
Diifeerences In C#
Diifeerences In C#Diifeerences In C#
Diifeerences In C#
rohit_gupta_mrt
 
For Beginners - C#
For Beginners - C#For Beginners - C#
For Beginners - C#
Snehal Harawande
 
Runtime Tools
Runtime ToolsRuntime Tools
Runtime Tools
ESUG
 
Scala for Java Programmers
Scala for Java ProgrammersScala for Java Programmers
Scala for Java Programmers
Eric Pederson
 
Java method present by showrov ahamed
Java method present by showrov ahamedJava method present by showrov ahamed
Java method present by showrov ahamed
Md Showrov Ahmed
 
Wrapper classes
Wrapper classes Wrapper classes
Core java by a introduction sandesh sharma
Core java by a introduction sandesh sharmaCore java by a introduction sandesh sharma
Core java by a introduction sandesh sharma
Sandesh Sharma
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better Validation
Tomer Gabel
 
Kpi driven-java-development-fn conf
Kpi driven-java-development-fn confKpi driven-java-development-fn conf
Kpi driven-java-development-fn conf
Anirban Bhattacharjee
 

Similar to InvokeBinder: Fluent Programming for Method Handles (20)

Reflection
ReflectionReflection
Reflection
 
Reflection
ReflectionReflection
Reflection
 
Reflection
ReflectionReflection
Reflection
 
Reflection
ReflectionReflection
Reflection
 
Reflection
ReflectionReflection
Reflection
 
Reflection
ReflectionReflection
Reflection
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
 
Developer testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticDeveloper testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing Fanatic
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
 
JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015
 
Session 08 - Arrays and Methods
Session 08 - Arrays and MethodsSession 08 - Arrays and Methods
Session 08 - Arrays and Methods
 
Diifeerences In C#
Diifeerences In C#Diifeerences In C#
Diifeerences In C#
 
For Beginners - C#
For Beginners - C#For Beginners - C#
For Beginners - C#
 
Runtime Tools
Runtime ToolsRuntime Tools
Runtime Tools
 
Scala for Java Programmers
Scala for Java ProgrammersScala for Java Programmers
Scala for Java Programmers
 
Java method present by showrov ahamed
Java method present by showrov ahamedJava method present by showrov ahamed
Java method present by showrov ahamed
 
Wrapper classes
Wrapper classes Wrapper classes
Wrapper classes
 
Core java by a introduction sandesh sharma
Core java by a introduction sandesh sharmaCore java by a introduction sandesh sharma
Core java by a introduction sandesh sharma
 
Leveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better ValidationLeveraging Scala Macros for Better Validation
Leveraging Scala Macros for Better Validation
 
Kpi driven-java-development-fn conf
Kpi driven-java-development-fn confKpi driven-java-development-fn conf
Kpi driven-java-development-fn conf
 

More from Charles Nutter

The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018
Charles Nutter
 
Down the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM WonderlandDown the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM Wonderland
Charles Nutter
 
Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016
Charles Nutter
 
JRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVMJRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVM
Charles Nutter
 
JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015
Charles Nutter
 
Fast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible JavaFast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible Java
Charles Nutter
 
Open Source Software Needs You!
Open Source Software Needs You!Open Source Software Needs You!
Open Source Software Needs You!
Charles Nutter
 
Over 9000: JRuby in 2015
Over 9000: JRuby in 2015Over 9000: JRuby in 2015
Over 9000: JRuby in 2015
Charles Nutter
 
Doing Open Source the Right Way
Doing Open Source the Right WayDoing Open Source the Right Way
Doing Open Source the Right Way
Charles Nutter
 
JRuby: The Hard Parts
JRuby: The Hard PartsJRuby: The Hard Parts
JRuby: The Hard Parts
Charles Nutter
 
Bringing Concurrency to Ruby - RubyConf India 2014
Bringing Concurrency to Ruby - RubyConf India 2014Bringing Concurrency to Ruby - RubyConf India 2014
Bringing Concurrency to Ruby - RubyConf India 2014
Charles Nutter
 
Beyond JVM - YOW! Sydney 2013
Beyond JVM - YOW! Sydney 2013Beyond JVM - YOW! Sydney 2013
Beyond JVM - YOW! Sydney 2013
Charles Nutter
 
Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013
Charles Nutter
 
Beyond JVM - YOW Melbourne 2013
Beyond JVM - YOW Melbourne 2013Beyond JVM - YOW Melbourne 2013
Beyond JVM - YOW Melbourne 2013
Charles Nutter
 
Down the Rabbit Hole
Down the Rabbit HoleDown the Rabbit Hole
Down the Rabbit Hole
Charles Nutter
 
The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013
Charles Nutter
 
High Performance Ruby - E4E Conference 2013
High Performance Ruby - E4E Conference 2013High Performance Ruby - E4E Conference 2013
High Performance Ruby - E4E Conference 2013
Charles Nutter
 
Invokedynamic in 45 Minutes
Invokedynamic in 45 MinutesInvokedynamic in 45 Minutes
Invokedynamic in 45 Minutes
Charles Nutter
 
Invokedynamic: Tales from the Trenches
Invokedynamic: Tales from the TrenchesInvokedynamic: Tales from the Trenches
Invokedynamic: Tales from the Trenches
Charles Nutter
 
Why JRuby? - RubyConf 2012
Why JRuby? - RubyConf 2012Why JRuby? - RubyConf 2012
Why JRuby? - RubyConf 2012
Charles Nutter
 

More from Charles Nutter (20)

The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018
 
Down the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM WonderlandDown the Rabbit Hole: An Adventure in JVM Wonderland
Down the Rabbit Hole: An Adventure in JVM Wonderland
 
Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016Ruby Performance - The Last Mile - RubyConf India 2016
Ruby Performance - The Last Mile - RubyConf India 2016
 
JRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVMJRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVM
 
JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015
 
Fast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible JavaFast as C: How to Write Really Terrible Java
Fast as C: How to Write Really Terrible Java
 
Open Source Software Needs You!
Open Source Software Needs You!Open Source Software Needs You!
Open Source Software Needs You!
 
Over 9000: JRuby in 2015
Over 9000: JRuby in 2015Over 9000: JRuby in 2015
Over 9000: JRuby in 2015
 
Doing Open Source the Right Way
Doing Open Source the Right WayDoing Open Source the Right Way
Doing Open Source the Right Way
 
JRuby: The Hard Parts
JRuby: The Hard PartsJRuby: The Hard Parts
JRuby: The Hard Parts
 
Bringing Concurrency to Ruby - RubyConf India 2014
Bringing Concurrency to Ruby - RubyConf India 2014Bringing Concurrency to Ruby - RubyConf India 2014
Bringing Concurrency to Ruby - RubyConf India 2014
 
Beyond JVM - YOW! Sydney 2013
Beyond JVM - YOW! Sydney 2013Beyond JVM - YOW! Sydney 2013
Beyond JVM - YOW! Sydney 2013
 
Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013
 
Beyond JVM - YOW Melbourne 2013
Beyond JVM - YOW Melbourne 2013Beyond JVM - YOW Melbourne 2013
Beyond JVM - YOW Melbourne 2013
 
Down the Rabbit Hole
Down the Rabbit HoleDown the Rabbit Hole
Down the Rabbit Hole
 
The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013The Future of JRuby - Baruco 2013
The Future of JRuby - Baruco 2013
 
High Performance Ruby - E4E Conference 2013
High Performance Ruby - E4E Conference 2013High Performance Ruby - E4E Conference 2013
High Performance Ruby - E4E Conference 2013
 
Invokedynamic in 45 Minutes
Invokedynamic in 45 MinutesInvokedynamic in 45 Minutes
Invokedynamic in 45 Minutes
 
Invokedynamic: Tales from the Trenches
Invokedynamic: Tales from the TrenchesInvokedynamic: Tales from the Trenches
Invokedynamic: Tales from the Trenches
 
Why JRuby? - RubyConf 2012
Why JRuby? - RubyConf 2012Why JRuby? - RubyConf 2012
Why JRuby? - RubyConf 2012
 

Recently uploaded

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
James Anderson
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
Aftab Hussain
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems S.M.S.A.
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
Uni Systems S.M.S.A.
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Nexer Digital
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Paige Cruz
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
DianaGray10
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
Pierluigi Pugliese
 

Recently uploaded (20)

UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
Alt. GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using ...
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
 
20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
 
Microsoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdfMicrosoft - Power Platform_G.Aspiotis.pdf
Microsoft - Power Platform_G.Aspiotis.pdf
 
Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?Elizabeth Buie - Older adults: Are we really designing for our future selves?
Elizabeth Buie - Older adults: Are we really designing for our future selves?
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024By Design, not by Accident - Agile Venture Bolzano 2024
By Design, not by Accident - Agile Venture Bolzano 2024
 

InvokeBinder: Fluent Programming for Method Handles

  • 3. Me • "Charles Oliver Nutter" <headius@headius.com> • @headius • Red Hat, JBoss, “Research and Prototyping” (nee Polyglot)
  • 6. Argument and return value manipulation
  • 7. Flow control and exception handling
  • 8. And the JVM optimizes it away (Eventually…sometimes…)
  • 9. So it’s all good, right?
  • 10. MethodHandle nativeTarget = MethodHandles.findVirtual(targetClass, targetName, targetType); // handle return value
 if (nativeReturn == long.class) {
 MethodHandle returnFilter = MethodHandles.insertArguments(
 MethodHandles.findStatic(RubyFixnum.class, “newFixnum", MethodType.methodType(RubyFixnum.class, Ruby.class, long.class)),
 0,
 runtime); nativeTarget = MethodHandles.filterReturnValue(nativeTarget, returnFilter);
  • 12. MethodHandle nativeTarget = findVirtual(targetClass, targetName, targetType); // handle return value
 if (nativeReturn == long.class) {
 // native integral type, produce a Fixnum
 MethodHandle returnFilter = insertArguments(
 findStatic(RubyFixnum.class, “newFixnum", methodType(RubyFixnum.class, Ruby.class, long.class)),
 0,
 runtime); nativeTarget = filterReturnValue(nativeTarget, returnFilter); ...
  • 13. • insert • drop • cast • invoke Have to start at the target and work back
  • 15. MethodHandle nativeTarget = findVirtual(targetClass, targetName, targetType); // handle return value
 if (nativeReturn == long.class
 || nativeReturn == short.class
 || nativeReturn == char.class
 || nativeReturn == int.class
 || nativeReturn == long.class) {
 // native integral type, produce a Fixnum
 nativeTarget = explicitCastArguments(nativeTarget, methodType(long.class, targetArgs));
 MethodHandle returnFilter = insertArguments(
 findStatic(RubyFixnum.class, “newFixnum", methodType(RubyFixnum.class, Ruby.class, long.class)),
 0,
 runtime); nativeTarget = filterReturnValue(nativeTarget, returnFilter); ...
  • 17. public Object tryFinally(MethodHandle target, MethodHandle post) throws Throwable {
 try {
 return target.invoke();
 } finally {
 post.invoke();
 }
 }
  • 19. try/finally 1. Like javac, finally path must be duplicated 2. Normal path invokes post, returns result of body 3. Exceptional path drops return, invokes post, re-raises exception 4. Now do this entirely on call stack, with no temp vars
  • 20. public Object tryFinally(MethodHandle target, MethodHandle post) throws Throwable {
 try {
 return target.invoke();
 } finally {
 post.invoke();
 }
 }
  • 21. MethodHandle exceptionHandler = Binder
 .from(target.type().insertParameterTypes(0, Throwable.class).changeReturnType(void.class))
 .drop(0)
 .invoke(post);
 
 MethodHandle rethrow = Binder
 .from(target.type().insertParameterTypes(0, Throwable.class))
 .fold(exceptionHandler)
 .drop(1, target.type().parameterCount())
 .throwException();
 
 target = MethodHandles.catchException(target, Throwable.class, rethrow);
 
 // if target returns a value, we must return it regardless of post
 MethodHandle realPost = post;
 if (target.type().returnType() != void.class) {
 // modify post to ignore return value
 MethodHandle newPost = Binder
 .from(target.type().insertParameterTypes(0, target.type().returnType()).changeReturnType(void.class))
 .drop(0)
 .invoke(post);
 
 // fold post into an identity chain that only returns the value
 realPost = Binder
 .from(target.type().insertParameterTypes(0, target.type().returnType()))
 .fold(newPost)
 .drop(1, target.type().parameterCount())
 .identity();
 }
 
 return MethodHandles.foldArguments(realPost, target);
  • 23. The Problems • Verbosity • Composition in reverse • MethodType must match exactly all the way through • Many common patterns are complicated to represent • Argument manipulation is by absolute offsets • Varargs and argument collection often need fixed size • Transforms can’t be easily shared across signatures
  • 27. Binder • Binder.from(type) produces a new Binder (which is immutable) • Binder#drop, insert, permute, etc act in call-forward direction • Binder#fold, filter, catchException, etc take additional handles • Endpoints: invoke*, set/getField, arraySet/Get, constant, identity, nop, branch (guardWithTest) • Utilities for larger constructs
  • 28. String value1 = System.getProperty("foo"); MethodHandle m1 = lookup         .findStatic(System.class, "getProperty", MethodType.methodType(String.class, String.class)); MethodHandle m2 = Binder.from(String.class, String.class)         .invokeStatic(lookup, System.class, "getProperty"); Static Method Lookup can also be passed into Binder#withLookup, eliminating this param
  • 29. PrintStream out1 = System.out; MethodHandle m3 = lookup         .findStaticGetter(System.class, "out", PrintStream.class); MethodHandle m4 = Binder.from(PrintStream.class)         .getStatic(lookup, System.class, "out"); Static Field Get
  • 30. class MyStruct {     public String name; } MyStruct ms = new MyStruct(); MethodHandle m5 = lookup         .findSetter(MyStruct.class, "name", String.class); MethodHandle m6 = Binder.from(void.class, MyStruct.class, String.class)         .setField(lookup, "name"); Instance Field Set No target type; Binder knows it already!
  • 31. MethodHandle m9 = lookup         .findStatic(Demo1.class, "twoArgs",                 MethodType.methodType(String.class, String.class, String.class)); m9 = MethodHandles.dropArguments(m9, 2, String.class); MethodHandle m10 = Binder.from(String.class, String.class, String.class, String.class)         .drop(2)         .invokeStatic(lookup, Demo1.class, "twoArgs"); m10.invoke("one", "two", "three"); // => "[one,two]" Deleting Args
  • 32. MethodHandle m11 = lookup         .findStatic(Demo1.class, "twoArgs",                 MethodType.methodType(String.class, String.class, String.class)); m11 = MethodHandles.permuteArguments(        m11,         MethodType.methodType(String.class, String.class, String.class, int.class),         1, 0); MethodHandle m12 = Binder.from(String.class, String.class, String.class, int.class)        .permute(1, 0)         .invokeStatic(lookup, Demo1.class, "initials"); m12.invoke("one", "two", 3); // => "[two,one]" Permute Args
  • 33. MethodHandle m13 = lookup         .findStatic(Demo1.class, "threeArgs",                 MethodType.methodType(String.class, String.class, String.class, String.class)); MethodHandle combiner = lookup         .findStatic(Demo1.class, "initials",                 MethodType.methodType(String.class, String.class, String.class)); m13 = MethodHandles.foldArguments(m13, combiner); MethodHandle m14 = Binder.from(String.class, String.class, String.class)         .fold(                 Binder                         .from(String.class, String.class, String.class)                         .invokeStatic(lookup, Demo1.class, "initials")         )         .invokeStatic(lookup, Demo1.class, "threeArgs"); m14.invoke("Charles", "Nutter"); // => ["CN", "Charles", "Nutter"] Fold
  • 34. MethodHandle m15 = lookup         .findStatic(Demo1.class, "twoArgs",                 MethodType.methodType(String.class, String.class, String.class)); MethodHandle filter = lookup         .findStatic(Demo1.class, "upcase",                 MethodType.methodType(String.class, String.class)); m15 = MethodHandles.filterArguments(m15, 0, filter, filter); MethodHandle m16 = Binder.from(String.class, String.class, String.class)         .filter(0,                  Binder.from(String.class, String.class)                         .invokeStatic(lookup, Demo1.class, "upcase")         )         .invokeStatic(lookup, Demo1.class, "twoArgs"); m16.invoke("hello", "world"); // => ["HELLO", "WORLD"] Filter
  • 35. MethodHandle m21 = Binder.from(String.class, int.class, String.class)         .branch(                 Binder.from(boolean.class, int.class, String.class)                         .drop(1)                         .invokeStatic(lookup, Demo1.class, "upOrDown"),                 Binder.from(String.class, int.class, String.class)                         .drop(0)                         .invokeStatic(lookup, Demo1.class, "upcase"),                 Binder.from(String.class, int.class, String.class)                         .drop(0)                         .invokeStatic(lookup, Demo1.class, "downcase")         ); m21.invoke(1, "MyString"); // => "MYSTRING" m21.invoke(0, "MyString"); // => "mystring" Boolean Branch
  • 36. Debugging! handle = Binder
 .from(String.class, Object.class, double.class, String.class)
 .dropFirst(2)
 .printType()
 .insert(1, "world")
 .invoke(target); No-op, just prints out current type
  • 37. MethodHandle handle = Binder
 .from(void.class, String[].class)
 .tryFinally(post)
 .invokeStatic(LOOKUP, BinderTest.class, "setZeroToFoo");
  • 38. • Binder aggregates MethodType, list of transforms • While building • MethodType is transformed (and verified or cast) • Transform stack pushes down a transform • At endpoint transform stack is played in reverse • And verified or cast (perhaps redundant)
  • 39. /**
 * Filter incoming arguments, starting at the given index, replacing * each with the result of calling the associated function in the * given list.
 *
 * @param index the index of the first argument to filter
 * @param functions the array of functions to transform the arguments
 * @return a new Binder
 */
 public Binder filter(int index, MethodHandle... functions) {
 return new Binder(this, new Filter(index, functions));
 }
  • 40. public class Filter extends Transform {
 
 private final int index;
 private final MethodHandle[] functions;
 
 public Filter(int index, MethodHandle... functions) {
 this.index = index;
 this.functions = functions;
 }
 
 public MethodHandle up(MethodHandle target) {
 return MethodHandles.filterArguments(target, index, functions);
 }
 
 public MethodType down(MethodType type) {
 for (int i = 0; i < functions.length; i++) {
 type = type.changeParameterType(index + i, functions[i].type().returnType());
 }
 return type;
 }
 
 public String toString() {
 return "fold args from " + index + " with " + Arrays.toString(functions);
 }
 }
  • 42. Argument juggling sucks MethodHandle handle2 = Binder
 .from(Subjects.StringIntegerIntegerIntegerString.type())
 .collect(1, 3, Integer[].class)
 .invoke(Subjects.StringIntegersStringHandle); If I need to change signature, all my offsets break
  • 43. Need a representation of an argument list…not just types
  • 44. Signature • Combines MethodType with argument names • Perform argument manipulations by name, not offset • Regex-based operations: collect, drop, permute • Conversion: signature1.to(signature2) => permute offsets • Type checking, pretty toString, other niceties
  • 45. Signature sig = Signature
 .returning(String.class)
 .asFold(Object.class); 
 assertEquals(Object.class, sig.type().returnType());
  • 46. Signature sig = Signature
 .returning(String.class)
 .appendArg("obj", Object.class)
 .appendArg("num", int.class)
 .insertArg("num", "flo", float.class);
 
 assertEquals("(Object obj, float flo, int num)String", sig.toString());
  • 47. private static final Signature stringObjectInt = Signature
 .returning(String.class)
 .appendArg("obj", Object.class)
 .appendArg("num", int.class);
  • 48. Signature sig = stringObjectInt
 .appendArg("flo", float.class)
 .appendArg("dub", double.class)
 .permute("obj", "dub");
 
 assertEquals("(Object obj, double dub)String", sig.toString());

  • 49. int[] permuteInts = stringObjectInt
 .appendArg("flo", float.class)
 .appendArg("dub", double.class)
 .to(stringObjectInt);
 
 assertArrayEquals(new int[] {0, 1}, permuteInts);
  • 50. int[] permuteInts = stringObjectInt
 .appendArg("flo", float.class)
 .appendArg("dub", double.class)
 .to(".*o.*");
 
 assertArrayEquals(new int[] {0, 2}, permuteInts);
 
 permuteInts = stringObjectInt
 .appendArg("flo", float.class)
 .appendArg("dub", double.class)
 .to("num", "dub");
 
 assertArrayEquals(new int[] {1, 3}, permuteInts);
  • 51. Signature sig = stringObjectInt
 .appendArg("flo", float.class)
 .appendArg("dub", double.class)
 .exclude("obj", "dub");
 
 assertEquals("(int num, float flo)String", sig.toString());
  • 52. public static final Signature StringIntegerIntegerIntegerString = Signature
 .returning(String.class)
 .appendArg("a", String.class)
 .appendArg("b1", Integer.class)
 .appendArg("b2", Integer.class)
 .appendArg("b3", Integer.class)
 .appendArg("c", String.class); Signature oldSig = Subjects.StringIntegerIntegerIntegerString;
 Signature newSig = oldSig.collect("bs", "b.*");
 
 assertEquals(Integer[].class, newSig.argType(1));
 assertEquals("bs", newSig.argName(1));
 assertEquals(3, newSig.argCount());
 assertEquals("c", newSig.argName(2));
  • 54. SmartBinder • All operations from Binder, but Signature-aware • Variable-length argument lists • Regex-based transforms • Result is a SmartHandle (MethodHandle + Signature)
  • 55. MethodHandle StringIntegersStringHandle = Binder
 .from(String.class, String.class, Integer[].class, String.class)
 .invokeStaticQuiet(LOOKUP, Subjects.class, "stringIntegersString");
  • 56. MethodHandle StringIntegersStringHandle = Binder
 .from(String.class, String.class, Integer[].class, String.class)
 .invokeStaticQuiet(LOOKUP, Subjects.class, "stringIntegersString"); Signature oldSig = Subjects.StringIntegerIntegerIntegerString;
 
 SmartHandle handle = SmartBinder
 .from(oldSig)
 .collect("bs", "b.*")
 .invoke(Subjects.StringIntegersStringHandle);
  • 57. Signature oldSig = Subjects.StringIntegerIntegerIntegerString;
 
 SmartHandle handle = SmartBinder.from(oldSig)
 .drop("b1")
 .drop("b2")
 .drop("b3")
 .insert(1, "bs", new Integer[]{1, 2, 3})
 .invoke(Subjects.StringIntegersStringHandle);
  • 58. MethodHandle target = Subjects.concatHandle();
 MethodHandle filter = MethodHandles.insertArguments(Subjects.concatHandle(), 1, "goodbye");
 MethodHandle handle = SmartBinder
 .from(String.class, arrayOf(“arg1", “arg2”), String.class, String.class)
 .filter("arg.*", filter)
 .invoke(target).handle();
  • 59. Signature.returning(IRubyObject.class)
 .appendArg("context", ThreadContext.class)
 .appendArg("caller", IRubyObject.class)
 .appendArg("self", IRubyObject.class)
 .appendArg("arg0", IRubyObject.class)
 .appendArg("arg1", IRubyObject.class)
 .appendArg("arg2", IRubyObject.class)
  • 61. MethodHandle stringInt = Binder
 .from(String.class, int.class)
 .invokeStaticQuiet(LOOKUP, Integer.class, “toString"); MethodHandle handle = SmartBinder
 .from(String.class, "i", int.class)
 .fold("s", stringInt)
 .dropLast()
 .identity()
 .handle();
 
 assertEquals(MethodType.methodType(String.class, int.class), handle.type());
 assertEquals("15", (String)handle.invokeExact(15));
  • 62. Status • Binder supports nearly all j.l.i.MethodHandle(s) operations • Signature has a wide array of transforms • SmartBinder has what I needed
  • 63. TODO • Analyze transform chain to emit more efficient handles • Fill out missing functionality, tests, docs • More utility forms • Generate bytecode or .java callable for non-indy • “Interpreted” [Smart]Binder#call like LFs for non-bytecode-loading • More extensive debugging (current transform stack, visual graph)
  • 64. Thank you! • "Charles Oliver Nutter" <headius@headius.com> • @headius • https://github.com/headius/invokebinder • Maven: com.headius:invokebinder