• JRuby developer for 20 years • Java developer for nearly 30 years • Funding JRuby and other OSS with commercial support contracts • Stickers and business cards
and improving with latest JDK features • Ensure community is healthy and growing • Headius Enterprises founder • Provide commercial support to users of JRuby + other OSS • Exploring other ways to fund JRuby + other OSS
Ruby compatibility and experience • Standard Ruby libs, frameworks, work fl ow all are the same • JVM for Ruby • Bring the best of the JVM to the Ruby world • Literally every single OpenJDK project is useful to us
run on any JVM, anywhere • Only JVM features, modulo some native call-outs (POSIX etc) • Deployable on existing servers, desktops, Android • JRuby supports Windows (and a dozen other esoteric platforms) • Truf fl eRuby is limited to GraalVM on Mac, Linux • Depends on native extensions, heavy down/upcalls hurt perf • Truf fl e startup/warmup are even worse than JRuby
• Introduction to InvokeBinder ( fl uent API for MethodHandles) • Implementing a simple language with MethodHandles • https://archive.fosdem.org/2018/schedule/event/method_handles/
is heavily dynamic, in sometimes surprising ways • JRuby and Indy have evolved together • Still need to support non-Indy mode • Startup, warmup, memory impact can be large • Odd platforms have issues e.g. Android, OpenJ9
• Not as straightforward as it seems • Different targets: Ruby, Java, native, ... • Validation and binding: Mutable types in Ruby, overloads in Java • Adapting argument layouts (optionals, varargs, keywords) • Indy allows all of these adaptations to inline!
identity (passive) Type/method table modi fi cation (active) Direct binding, no boxing, inlines well Ruby variable arity Ruby with keyword args Type identity (passive) Type/method table modi fi cation (active) Direct binding, fully boxed, usually does not EA. May split entry point in future. Core JRuby method (Java) Type identity (passive) Type/method table modi fi cation (active) Matched arity binds directly. Mismatched arity through varargs box. Normal Java/JVM method Same as above, but also mismatched argument types and Java overloads Single-arity matched args is mostly direct. Overloads use varargs. Native downcall Like Ruby to Ruby; overloads are separate methods. JNR: indy all the way to JNI call. Panama: indy all the way. Ruby special calls (super, re fi ned) Partial hierarchy check (super). Local scope method table check. Fully unoptimized currently. Calls with blocks (lambdas) Same as above; block can replace trailing interface arg like lambda. Indirect block dispatch, poor inlining as with lambda.
at org.jruby.dist/org.jruby.java.invokers.StaticMethodInvoker.call(StaticMethodInvoker.java:23) at org.jruby.dist/org.jruby.java.invokers.StaticMethodInvoker.call(StaticMethodInvoker.java:85) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:90) at org.jruby.dist/org.jruby.ir.instructions.CallBase.interpret(CallBase.java:556) at org.jruby.dist/org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:372) at org.jruby.dist/org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:66) at org.jruby.dist/org.jruby.ir.interpreter.Interpreter.interpretFrameScope(Interpreter.java:174) at org.jruby.dist/org.jruby.ir.interpreter.Interpreter.INTERPRET_METHOD(Interpreter.java:145) at org.jruby.dist/org.jruby.internal.runtime.methods.InterpretedIRMethod.call(InterpretedIRMethod.java:130) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:193) at org.jruby.dist/org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:352) at org.jruby.dist/org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:66) at org.jruby.dist/org.jruby.ir.interpreter.Interpreter.interpretFrameScope(Interpreter.java:174) at org.jruby.dist/org.jruby.ir.interpreter.Interpreter.INTERPRET_METHOD(Interpreter.java:145) at org.jruby.dist/org.jruby.internal.runtime.methods.InterpretedIRMethod.call(InterpretedIRMethod.java:130) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:193) at org.jruby.dist/org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:352) at org.jruby.dist/org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:66) at org.jruby.dist/org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:120)
at org.jruby.dist/org.jruby.java.invokers.StaticMethodInvoker.call(StaticMethodInvoker.java:23) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:75) at blah.invokeOther5:dumpStack(blah.rb:8) at blah .️ ❤ def bar #2(blah.rb:8) at org.jruby.dist/org.jruby.internal.runtime.methods.CompiledIRMethod.call(CompiledIRMethod.java:139) at org.jruby.dist/org.jruby.internal.runtime.methods.CompiledIRMethod.call(CompiledIRMethod.java:212) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:193) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.fcall(CachingCallSite.java:199) at blah.invokeOther1:bar(blah.rb:4) at blah .️ ❤ def foo #1(blah.rb:4) at org.jruby.dist/org.jruby.internal.runtime.methods.CompiledIRMethod.call(CompiledIRMethod.java:215) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:193) at org.jruby.dist/org.jruby.runtime.callsite.CachingCallSite.fcall(CachingCallSite.java:199) at blah.invokeOther0:foo(blah.rb:11) at blah .️ ❤ {} \=\^main\_ #0(blah.rb:11)
java.lang.invoke.LambdaForm$MH/0x00000070015ab000.invoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015abc00.reinvoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015ac000.guard(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015abc00.reinvoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015ac000.guard(java.base@21/LambdaForm$MH) at java.lang.invoke.Invokers$Holder.linkToCallSite(java.base@21/Invokers$Holder) at blah .️ ❤ def foo #1(blah.rb:2) at java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(java.base@21/DirectMethodHandle$Holder) at java.lang.invoke.LambdaForm$MH/0x00000070015ab000.invoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015abc00.reinvoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015ac000.guard(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015abc00.reinvoke(java.base@21/LambdaForm$MH) at java.lang.invoke.LambdaForm$MH/0x00000070015ac000.guard(java.base@21/LambdaForm$MH) at java.lang.invoke.Invokers$Holder.linkToCallSite(java.base@21/Invokers$Holder) at blah .️ ❤ {} \=\^main\_ #0(blah.rb:9)
def bar #3(blah.rb:6) at blah .️ ❤ def foo #2(blah.rb:2) at blah .️ ❤ {} \=\^main\_ #1(blah.rb:9) at org.jruby.dist/org.jruby.runtime.CompiledIRBlockBody.yieldDirect(CompiledIRBlockBody.java:151) at org.jruby.dist/org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:74) at org.jruby.dist/org.jruby.runtime.Block.yieldSpecific(Block.java:160) at org.jruby.dist/org.jruby.RubyFixnum.times(RubyFixnum.java:333) at blah .️ ❤ script(blah.rb:9)
java.lang.Thread.dumpStack(*args) end at java.base/java.lang.Thread.dumpStack(Thread.java:2210) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.jruby.dist/org.jruby.javasupport.JavaMethod.invokeDirectWithExceptionHandling(JavaMethod.java:290) at org.jruby.dist/org.jruby.javasupport.JavaMethod.invokeStaticDirect(JavaMethod.java:221) at org.jruby.dist/org.jruby.java.invokers.StaticMethodInvoker.call(StaticMethodInvoker.java:23) at blah .️ ❤ def bar #2(blah.rb:10) at blah .️ ❤ def foo #1(blah.rb:6)
a good pattern for rewriting code to indy • Splitting and specialization for blocks (lambdas) • Same problem as in Java, but pervasive • Splitting heuristics are an open research problem • Numeric unboxing • Dif fi cult to do without being too aggressive, generating too much code
table for accesses on fi rst instantiation • Load or generate class "shape" to map ivars to fi elds • Object array for spilled or dynamic variables • InvokeDynamic wires access directly to fi eld or array
overwritten • Globals are global, either constant or frequently modi fi ed • Indy call sites can constantize them effectively • Mutable call site folds value just fi ne • Invalidate globally based on constant or global name • Complex lookup forms like Foo::Bar::Baz could be improved
into indy site • Indy chooses from several forms to interpret speci fi cation • N overloads that stitch it together (up to 4 args) • Static bits from site, dynamic from args[] • Simple loop over static + dynamic args[] • Bail out for extremely large forms (>50 elements)
string "UTF-8", // encoding 16, // code range " was passed", // string "UTF-8", // encoding 16, // code range 55, // size estimate "UTF-8", // final encoding 0, // frozen? 0, // chilled? 5L, // specification (bit indicates a static piece) 3 // how many bits are relevant ] def foo(a) puts "the value #{a} was passed" end
Heap-based local variables • Direct walk to appropriate scope depth, retrieve/set var • Thread interrupt with safepoints, but invalidates on interrupt 🤔 • Other "constant dynamic" runtime utils used within JRuby
Negated comparison • != is not(==), !~ is not(=~) • Common forms: Kernel#loop, Integer#times • Anything could be compiled to IR (see my FOSDEM 2018 talk) • But impossible to reconstruct/simulate call stack
fi ling • LF frames included in pro fi les • LF adaptations look like separate call paths for same call • The more we use Indy, the harder it is to get accurate pro fi les
inject invokedynamic in Java code • Static CallSite.invoke gets us close, but dat Throwable 😵 • Build-time rewriting as a fi rst step? • Give up on Java and move all to Ruby?
in many gaps • Better adaptations for argument forms, overloads • Finally wire up Panama for native • Explore more intrinsic forms and specialization • Looking forward to working with OpenJDK projects more