SlideShare a Scribd company logo
1 of 65
Building Languages
    for the JVM
    Charles Oliver Nutter
Me

• Charles Oliver Nutter
• JRuby and JVM guy
• “headius” on most services
• headius@headius.com
Who Was I?

• Java EE architect
 • Successfully!
• Never wrote a parser
• Never wrote a compiler
• But I wanted to learn how...
You

• Java?
• Ruby?
• Python?
• C#?
• Other?
Why Create Languages?

• Nothing is perfect
• New problems need new solutions
• Language design can be fun
• Fame and fortune?
 • Not really.
Why Impl a Language?
• To learn it?
 • Sort of...
• To learn the target platform
 • Definitely!
• Fame and fortune?
 • Well...getting there...
Challenges

• Community
• Platform
• Specifications
• Resources
Community

• Investment in status quo
• Afraid to stand out
• Known quantities
• Everything else sucks
• Gotta get paid!
Platform
• Matching language semantics
 • JVM designed around Java
 • JVM hides underlying platform
• Challenging to use
 • Not bad...C/++ would be way worse
• Community may hate it ;-)
Specifications
•   Incomplete
    •   Ruby had none for years
    •   ...and no complete test suites
•   Difficult to implement
    •   Low level features
    •   Single-implementation quirks
•   Hard or impossible to optimize
Resources

• You gotta eat
• Not much money in language work
• Some parts are hard
• OSS is a necessity
Why JVM?

• Because I am lazy
• Because VMs are *hard*
• Because I can’t be awesome at everything
Ok, Why Really?
• Cross-platform
• Libraries
• Languages
• Memory management
• Tools
• OSS
Cross-platform

• OpenJDK: Linux, Windows, Solaris, OS X,
  xBSD
• J9: Linux, zLinux, AS/400, ...
• HP: OpenVMS, HP/UX, ...
• Dalvik (Android): Linux on ARM, x86
Libraries

• For any need, a dozen libraries
 • And a couple of them are good!
• Cross-platform
• Leading edge
Selection of languages
• Java
• Scala
• Clojure
• JRuby
• Mirah
• Jython, Groovy, Fantom, Kotlin, Ceylon, ...
Memory management

• Best GCs in the world
• Fastest object allocation
• Safe escape hatches like NIO
Tools

• Debugging
• Profiling
• Monitoring
• JVM internals
Open source?

• FOSS reference impl (OpenJDK)
• Mostly OSS libraries
• Heavy OSS culture
• Strong OSS influence in OpenJDK core
Case Study: JRuby
Ruby on the JVM

• All of Ruby’s power and beauty
• Solid VM underneath
• “Just another JVM language”
JVM Language
•   Full interop with Java
    •   Tricky to do...
    •   Very rewarding for 99% case
•   VM concerns solved
    •   No need to write a GC
    •   No need to write a JIT
    •   ...oh, but wait...
More than a JVM
        language
• Use native code where JDK fails us
• Paper over ugly bits like CLASSPATH
• Matching Ruby semantics exactly*
• Push JVM forward too!
Playing with JRuby

• Simple IRB demo
• JRuby on Rails - see Jano’s talk tomorrow
• JRuby performance
• PotC (???)
How did we do it?
JRuby Architecture

• Parser
• Abstract Syntax Tree (AST)
• Intermediate Representation (IR)
• Core classes
• Compiler
Parser

• Port of MRI’s Bison grammar
• “Jay” parser generator for Java
• Hand-written lexer
• Nearly as fast as the C version
 • ...once it gets going
system ~/projects/jruby $ jruby -y -e "1 + 1"
push	 state 0	 value null
reduce	 tate 0	 uncover 0	
       s                  rule (1) $$1 :
goto	 from state 0 to 2
push	 state 2	 value null
lex	 tate 2	 reading tIDENTIFIER	 value Token { Value=load,
   s
Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/
jruby/kernel.rb:6}
shift	from state 2 to 33
push	 state 33	 value Token { Value=load, Position=file:/Users/
headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6}
lex	 tate 33	 reading tSTRING_BEG	 value Token { Value=',
   s
Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/
jruby/kernel.rb:6}
reduce	 tate 33	 uncover 2	
       s                  rule (487) operation : tIDENTIFIER
goto	 from state 2 to 62
push	 state 62	 value Token { Value=load, Position=file:/Users/
headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6}
reduce	 tate 62	 uncover 62	 rule (252) $$6 :
       s
system ~/projects/jruby $ jruby -y -e "1 + 1"
push	 state 0	 value null
reduce	 tate 0	 uncover 0	
       s                  rule (1) $$1 :
goto	 from state 0 to 2
push	 state 2	 value null
lex	 tate 2	 reading tIDENTIFIER	 value Token { Value=load,
   s
Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/
jruby/kernel.rb:6}
shift	from state 2 to 33

         You will never need this.
push	 state 33	 value Token { Value=load, Position=file:/Users/
headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6}
lex	 tate 33	 reading tSTRING_BEG	 value Token { Value=',
   s
Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/
jruby/kernel.rb:6}
reduce	 tate 33	 uncover 2	
       s                  rule (487) operation : tIDENTIFIER
goto	 from state 2 to 62
push	 state 62	 value Token { Value=load, Position=file:/Users/
headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6}
reduce	 tate 62	 uncover 62	 rule (252) $$6 :
       s
public class RubyYaccLexer {
    public static final Encoding UTF8_ENCODING = UTF8Encoding.INSTANCE;
    public static final Encoding USASCII_ENCODING = USASCIIEncoding.INSTANCE;
    public static final Encoding ASCII8BIT_ENCODING = ASCIIEncoding.INSTANCE;
    
    private static ByteList END_MARKER = new ByteList(new byte[] {'_', 'E', 'N', 'D', '_', '_'});
    private static ByteList BEGIN_DOC_MARKER = new ByteList(new byte[] {'b', 'e', 'g', 'i', 'n'});
    private static ByteList END_DOC_MARKER = new ByteList(new byte[] {'e', 'n', 'd'});
    private static final HashMap<String, Keyword> map;

    static {
        map = new HashMap<String, Keyword>();

        map.put("end", Keyword.END);
        map.put("else", Keyword.ELSE);
        map.put("case", Keyword.CASE);
        map.put("ensure", Keyword.ENSURE);
        map.put("module", Keyword.MODULE);
        map.put("elsif", Keyword.ELSIF);
        map.put("def", Keyword.DEF);
        map.put("rescue", Keyword.RESCUE);
        map.put("not", Keyword.NOT);
        map.put("then", Keyword.THEN);
        map.put("yield", Keyword.YIELD);
        map.put("for", Keyword.FOR);
        map.put("self", Keyword.SELF);
        map.put("false", Keyword.FALSE);
    public enum Keyword {
        END ("end", Tokens.kEND, Tokens.kEND, LexState.EXPR_END),
        ELSE ("else", Tokens.kELSE, Tokens.kELSE, LexState.EXPR_BEG),
        CASE ("case", Tokens.kCASE, Tokens.kCASE, LexState.EXPR_BEG),
        ENSURE ("ensure", Tokens.kENSURE, Tokens.kENSURE, LexState.EXPR_BEG),
        MODULE ("module", Tokens.kMODULE, Tokens.kMODULE, LexState.EXPR_BEG),
        ELSIF ("elsif", Tokens.kELSIF, Tokens.kELSIF, LexState.EXPR_BEG),
        DEF ("def", Tokens.kDEF, Tokens.kDEF, LexState.EXPR_FNAME),
        RESCUE ("rescue", Tokens.kRESCUE, Tokens.kRESCUE_MOD, LexState.EXPR_MID),
        NOT ("not", Tokens.kNOT, Tokens.kNOT, LexState.EXPR_BEG),
        THEN ("then", Tokens.kTHEN, Tokens.kTHEN, LexState.EXPR_BEG),
        YIELD ("yield", Tokens.kYIELD, Tokens.kYIELD, LexState.EXPR_ARG),
        FOR ("for", Tokens.kFOR, Tokens.kFOR, LexState.EXPR_BEG),
        SELF ("self", Tokens.kSELF, Tokens.kSELF, LexState.EXPR_END),
        FALSE ("false", Tokens.kFALSE, Tokens.kFALSE, LexState.EXPR_END),
        RETRY ("retry", Tokens.kRETRY, Tokens.kRETRY, LexState.EXPR_END),
        RETURN ("return", Tokens.kRETURN, Tokens.kRETURN, LexState.EXPR_MID),
        TRUE ("true", Tokens.kTRUE, Tokens.kTRUE, LexState.EXPR_END),
        IF ("if", Tokens.kIF, Tokens.kIF_MOD, LexState.EXPR_BEG),
        DEFINED_P ("defined?", Tokens.kDEFINED, Tokens.kDEFINED, LexState.EXPR_ARG),
    private int yylex() throws IOException {
        int c;
        boolean spaceSeen = false;
        boolean commandState;
        
        if (lex_strterm != null) {
            int tok = lex_strterm.parseString(this, src);
            if (tok == Tokens.tSTRING_END || tok == Tokens.tREGEXP_END) {
                lex_strterm = null;
                setState(LexState.EXPR_END);
            }

            return tok;
        }

        commandState = commandStart;
        commandStart = false;

        loop: for(;;) {
            c = src.read();
            switch(c) {
            case '<':
                return   lessThan(spaceSeen);
            case '>':
                return   greaterThan();
            case '"':
                return   doubleQuote();
            case '`':
                return   backtick(commandState);
            case ''':
                return   singleQuote();
            case '?':
                return   questionMark();
            case '&':
                return   ampersand(spaceSeen);
            case '|':
                return   pipe();
            case '+':
                return   plus(spaceSeen);
    private int lessThan(boolean spaceSeen) throws IOException {
        int c = src.read();
        if (c == '<' && lex_state != LexState.EXPR_DOT && lex_state !=
LexState.EXPR_CLASS &&
                !isEND() && (!isARG() || spaceSeen)) {
            int tok = hereDocumentIdentifier();
            
            if (tok != 0) return tok;
        }
        
        determineExpressionState();
        
        switch (c) {
        case '=':
            if ((c = src.read()) == '>') {
                yaccValue = new Token("<=>", getPosition());
                return Tokens.tCMP;
%%
program       : {
                  lexer.setState(LexState.EXPR_BEG);
                  support.initTopLocalVariables();
              } top_compstmt {
  // ENEBO: Removed !compile_for_eval which probably is to reduce
warnings
                  if ($2 != null) {
                      /* last expression should not be void */
                      if ($2 instanceof BlockNode) {
                          support.checkUselessStatement($<BlockNod
e>2.getLast());
                      } else {
                          support.checkUselessStatement($2);
                      }
                  }
                  support.getResult().setAST(support.addRootNode($
2, support.getPosition($2)));
              }
stmt            : kALIAS fitem {
                    lexer.setState(LexState.EXPR_FNAME);
                } fitem {
                    $$ = support.newAlias($1.getPosition(), $2, $4);
                }
                | kALIAS tGVAR tGVAR {
                    $$ = new VAliasNode($1.getPosition(), (String)
$2.getValue(), (String) $3.getValue());
                }
                | kALIAS tGVAR tBACK_REF {
                    $$ = new VAliasNode($1.getPosition(), (String)
$2.getValue(), "$" + $<BackRefNode>3.getType());
                }
                | kALIAS tGVAR tNTH_REF {
                    support.yyerror("can't make alias for the number
variables");
                }
                | kUNDEF undef_list {
                    $$ = $2;
                }
                | stmt kIF_MOD expr_value {
                    $$ = new IfNode(support.getPosition($1),
support.getConditionNode($3), $1, null);
                }
  public Object yyparse (RubyYaccLexer yyLex) throws java.io.IOException {
    if (yyMax <= 0) yyMax = 256;"" " // initial size
    int yyState = 0, yyStates[] = new int[yyMax];" // state stack
    Object yyVal = null, yyVals[] = new Object[yyMax];" // value stack
    int yyToken = -1;" " " " " // current input
    int yyErrorFlag = 0;" " " " // #tokens to shift

    yyLoop: for (int yyTop = 0;; ++ yyTop) {
      if (yyTop >= yyStates.length) {" " " // dynamically increase
        int[] i = new int[yyStates.length+yyMax];
        System.arraycopy(yyStates, 0, i, 0, yyStates.length);
        yyStates = i;
        Object[] o = new Object[yyVals.length+yyMax];
        System.arraycopy(yyVals, 0, o, 0, yyVals.length);
        yyVals = o;
      }
      yyStates[yyTop] = yyState;
      yyVals[yyTop] = yyVal;
      if (yydebug != null) yydebug.push(yyState, yyVal);
        if (state == null) {
            yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]);
        } else {
            yyVal = state.execute(support, lexer, yyVal, yyVals, yyTop);
        }
states[23] = new ParserState() {
  public Object execute(ParserSupport support, RubyYaccLexer lexer, Object
yyVal, Object[] yyVals, int yyTop) {
                    yyVal = new
IfNode(support.getPosition(((Node)yyVals[-2+yyTop])),
support.getConditionNode(((Node)yyVals[0+yyTop])), ((Node)yyVals[-2+yyTop]),
null);
    return yyVal;
  }
};
                  Never look at this.
states[24] = new ParserState() {
  public Object execute(ParserSupport support, RubyYaccLexer lexer, Object
yyVal, Object[] yyVals, int yyTop) {
                    yyVal = new
IfNode(support.getPosition(((Node)yyVals[-2+yyTop])),
support.getConditionNode(((Node)yyVals[0+yyTop])), null,
((Node)yyVals[-2+yyTop]));
    return yyVal;
  }
};
AST

• Interpreted directly
• Specialized in places
• Large and rich
$ ast -e "a = true; if a; 2; else; 3; end"
AST:
RootNode 0
  BlockNode 0
     NewlineNode 0
       LocalAsgnNode:a 0
         TrueNode:true 0
     NewlineNode 0
       IfNode 0
         LocalVarNode:a 0
         NewlineNode 0
           FixnumNode 0
         NewlineNode 0
           FixnumNode 0
public class IfNode extends Node {
    private final Node condition;
    private final Node thenBody;
    private final Node elseBody;

    public IfNode(ISourcePosition position, Node   condition, Node thenBody,
Node elseBody) {
        super(position);
        
        assert condition != null : "condition is   not null";
//        assert thenBody != null : "thenBody is   not null";
//        assert elseBody != null : "elseBody is   not null";
        
        this.condition = condition;
        this.thenBody = thenBody;
        this.elseBody = elseBody;
    }
    @Override
    public IRubyObject interpret(Ruby runtime, ThreadContext context,
IRubyObject self, Block aBlock) {
        ISourcePosition position = getPosition();

        context.setFile(position.getFile());
        context.setLine(position.getStartLine());

        IRubyObject result =
             condition.interpret(runtime, context, self, aBlock);
        
        if (result.isTrue()) {
            return thenBody == null ?
                 runtime.getNil() :
                 thenBody.interpret(runtime, context, self, aBlock);
        } else {
            return elseBody == null ?
                 runtime.getNil() :
                 elseBody.interpret(runtime, context, self, aBlock);
        }
    }
IR (future work)

• Control flow graph
• Ruby-specific instruction set
• Optimizing compiler
• Ruby-level optimizations
jruby -e “1 + 1”
2011-11-04T05:23:09.375-03:00: IR_Printer:
  instrs:
  0	 %self = recv_self
  1	 %block(0:0) = recv_closure
  2	 file_name(-e)
  3	 line_num(0)
  4	 %v_0 = call(+, 1:fixnum, [1:fixnum])
  5	 return(%v_0)
2011-11-04T05:23:09.375-03:00: IR_Printer:
  live variables:
    %v_0: 4-5
a = 1; while a < 10; puts a; a += 1; end
2011-11-04T05:25:23.517-03:00: IR_Printer:
  instrs:
  0	%self = recv_self
  1	%block(0:0) = recv_closure
  2	file_name(-e)
  3	line_num(0)
  4	a(0:1) = 1:fixnum
  5	_LOOP_BEGIN_0:
  6	%v_1 = call(<, a(0:1), [10:fixnum])
  7	beq(%v_1, true, _ITER_BEGIN_0)
  8	%v_0 = nil
  9	jump _LOOP_END_0
  10	   _ITER_BEGIN_0:
  11	   %v_2 = call(puts, %self, [a(0:1)])
  12	   %v_3 = call(+, a(0:1), [1:fixnum])
  13	   a(0:1) = copy(%v_3)
  14	   %v_0 = copy(%v_3)
  15	   thread_poll
  16	   _ITER_END_0:
  17	   jump _LOOP_BEGIN_0
  18	   _LOOP_END_0:
  19	   return(%v_0)
2011-11-04T05:25:23.517-03:00: IR_Printer:
  live variables:
    %v_0: 8-19
    %v_1: 6-7
    %v_3: 12-14
2011-11-04T05:25:23.518-03:00: IRScope: ################## After
CFG Linearize##################
2011-11-04T05:25:23.518-03:00: IR_Printer:
----------------------------------------
2011-11-04T05:25:23.518-03:00: IR_Printer: Method [root]:
[script]:-e
2011-11-04T05:25:23.518-03:00: IR_Printer:
Graph:
BB [4:LBL_3]:>[7], <[3]
BB [1:LBL_1]:>[8,2]
BB [2:LBL_2]:>[3], <[1]
BB [7:_LOOP_END_0]:>[8], <[4]
BB [8:LBL_4]:<[1,7]
BB [3:_LOOP_BEGIN_0]:>[5,4], <[6,2]
BB [6:_ITER_END_0]:>[3], <[5]
BB [5:_ITER_BEGIN_0]:>[6], <[3]
2011-11-04T05:25:23.517-03:00: IR_Printer:
  instrs:
  0	%self = recv_self
  1	%block(0:0) = recv_closure
  2	file_name(-e)
  3	line_num(0)
  4	a(0:1) = 1:fixnum
  5	_LOOP_BEGIN_0:
  6	%v_1 = call(<, a(0:1), [10:fixnum])
  7	beq(%v_1, true, _ITER_BEGIN_0)
  8	%v_0 = nil
  9	jump _LOOP_END_0
  10	   _ITER_BEGIN_0:
  11	   %v_2 = call(puts, %self, [a(0:1)])
  12	   %v_3 = call(+, a(0:1), [1:fixnum])
  13	   a(0:1) = copy(%v_3)
  14	   %v_0 = copy(%v_3)
  15	   thread_poll
  16	   _ITER_END_0:
  17	   jump _LOOP_BEGIN_0
  18	   _LOOP_END_0:
  19	   return(%v_0)
2011-11-04T05:25:23.517-03:00: IR_Printer:
  live variables:
    %v_0: 8-19
    %v_1: 6-7
    %v_3: 12-14
2011-11-04T05:25:23.518-03:00: IR_Printer:
Instructions:
BB [4:LBL_3]
	 %v_0 = nil
BB [1:LBL_1]
BB [2:LBL_2]
	 %self = recv_self
	 %block(0:0) = recv_closure
	 file_name(-e)
	 line_num(0)
	 a(0:1) = 1:fixnum
BB [7:_LOOP_END_0]
	 return(%v_0)
BB [8:LBL_4]
	 return(nil)
Core classes

• Mostly Java-based
• Leverage JDK where possible
• Work around JDK where necessary
• Use Ruby when possible
@JRubyClass(name="Fixnum", parent="Integer", include="Precision")
public class RubyFixnum extends RubyInteger {
    
    public static RubyClass createFixnumClass(Ruby runtime) {
        RubyClass fixnum = runtime.defineClass("Fixnum", runtime.getInteger(),
                ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
    @JRubyMethod(name = "+")
    public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
        if (other instanceof RubyFixnum) {
            return addFixnum(context, (RubyFixnum)other);
        }
        return addOther(context, other);
    }
    private IRubyObject addFixnum(ThreadContext context, RubyFixnum other) {
        long otherValue = other.value;
        long result = value + otherValue;
        if (additionOverflowed(value, otherValue, result)) {
            return addAsBignum(context, other);
        }
        return newFixnum(context.getRuntime(), result);
    }
Compiler

• AST-walking
• ASM bytecode library
• Minimal optimizations
• Invokedynamic really helps
• IR offers new opportunities
jruby --bytecode -e “1 + 1”
ALOAD 0
INVOKEVIRTUAL ruby/__dash_e__.getCallSite0
ALOAD 1
ALOAD 2
ALOAD 1
GETFIELD org/jruby/runtime/ThreadContext.runtime
INVOKESTATIC org/jruby/RubyFixnum.one
LDC 1
INVOKEVIRTUAL org/jruby/runtime/CallSite.call
ARETURN
public class ASTCompiler {
    private boolean isAtRoot = true;

    public void compileBody(Node node, BodyCompiler context, boolean expr) {
        Node oldBodyNode = currentBodyNode;
        currentBodyNode = node;
        compile(node, context, expr);
        currentBodyNode = oldBodyNode;
    }
    
    public void compile(Node node, BodyCompiler context, boolean expr) {
        if (node == null) {
            if (expr) context.loadNil();
            return;
        }
        switch (node.getNodeType()) {
            case ALIASNODE:
                compileAlias((AliasNode) node, context, expr);
                break;
            case ANDNODE:
                compileAnd(node, context, expr);
                break;
    public void compileIf(Node node, BodyCompiler context, final boolean expr) {
        final IfNode ifNode = (IfNode) node;

        // optimizations if we know ahead of time it will always be true or false
        Node actualCondition = ifNode.getCondition();
        while (actualCondition instanceof NewlineNode) {
            actualCondition = ((NewlineNode)actualCondition).getNextNode();
        }

        if (actualCondition.getNodeType().alwaysTrue()) {
            // compile condition as non-expr and just compile "then" body
            compile(actualCondition, context, false);
            compile(ifNode.getThenBody(), context, expr);
        } else if (actualCondition.getNodeType().alwaysFalse()) {
            // always false or nil
            compile(ifNode.getElseBody(), context, expr);
        } else {
            BranchCallback trueCallback = new BranchCallback() {
                public void branch(BodyCompiler context) {
                    if (ifNode.getThenBody() != null) {
                        compile(ifNode.getThenBody(), context, expr);
                    } else {
                        if (expr) context.loadNil();
                    }
                }
            };

            BranchCallback falseCallback = new BranchCallback() {
                public void branch(BodyCompiler context) {
                    if (ifNode.getElseBody() != null) {
                        compile(ifNode.getElseBody(), context, expr);
                    } else {
                        if (expr) context.loadNil();
                    }
                }
            };
            
            // normal
            compile(actualCondition, context, true);
            context.performBooleanBranch(trueCallback, falseCallback);
        }
public abstract class BaseBodyCompiler implements BodyCompiler {
    protected SkinnyMethodAdapter method;
    protected VariableCompiler variableCompiler;
    protected InvocationCompiler invocationCompiler;
    protected int argParamCount;
    protected Label[] currentLoopLabels;
    protected Label scopeStart = new Label();
    protected Label scopeEnd = new Label();
    protected Label redoJump;
    protected boolean inNestedMethod = false;
    private int lastLine = -1;
    private int lastPositionLine = -1;
    protected StaticScope scope;
    protected ASTInspector inspector;
    protected String methodName;
    protected String rubyName;
    protected StandardASMCompiler script;
    public void performBooleanBranch(BranchCallback trueBranch,
                                      BranchCallback falseBranch) {
        Label afterJmp = new Label();
        Label falseJmp = new Label();

        // call isTrue on the result
        isTrue();

        method.ifeq(falseJmp); // EQ == 0 (i.e. false)
        trueBranch.branch(this);
        method.go_to(afterJmp);

        // FIXME: optimize for cases where we have no false branch
        method.label(falseJmp);
        falseBranch.branch(this);

        method.label(afterJmp);
    }
More Demos!
• JRuby + JVM flags
• JRuby concurrency
• Invokedynamic (Java 7)
• Redcar Editor
• Ruboto (Android)
• VisualVM
Gracias!

• Charles Oliver Nutter
• “headius” on most services
• headius@headius.com

More Related Content

What's hot

What's hot (20)

JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015JRuby and Invokedynamic - Japan JUG 2015
JRuby and Invokedynamic - Japan JUG 2015
 
Guild Prototype
Guild PrototypeGuild Prototype
Guild Prototype
 
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
 
Type Profiler: An Analysis to guess type signatures
Type Profiler: An Analysis to guess type signaturesType Profiler: An Analysis to guess type signatures
Type Profiler: An Analysis to guess type signatures
 
JavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for DummiesJavaOne 2011 - JVM Bytecode for Dummies
JavaOne 2011 - JVM Bytecode for Dummies
 
PHP7
PHP7PHP7
PHP7
 
JRuby: The Hard Parts
JRuby: The Hard PartsJRuby: The Hard Parts
JRuby: The Hard Parts
 
TRICK2015 results
TRICK2015 resultsTRICK2015 results
TRICK2015 results
 
Over 9000: JRuby in 2015
Over 9000: JRuby in 2015Over 9000: JRuby in 2015
Over 9000: JRuby in 2015
 
JRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVMJRuby 9000 - Optimizing Above the JVM
JRuby 9000 - Optimizing Above the JVM
 
Slot Composition
Slot CompositionSlot Composition
Slot Composition
 
Slot Composition
Slot CompositionSlot Composition
Slot Composition
 
A Taste of Clojure
A Taste of ClojureA Taste of Clojure
A Taste of Clojure
 
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
 
Ahead-Of-Time Compilation of Java Applications
Ahead-Of-Time Compilation of Java ApplicationsAhead-Of-Time Compilation of Java Applications
Ahead-Of-Time Compilation of Java Applications
 
Java 7 Whats New(), Whats Next() from Oredev
Java 7 Whats New(), Whats Next() from OredevJava 7 Whats New(), Whats Next() from Oredev
Java 7 Whats New(), Whats Next() from Oredev
 
The State of Managed Runtimes 2013, by Attila Szegedi
The State of Managed Runtimes 2013, by Attila SzegediThe State of Managed Runtimes 2013, by Attila Szegedi
The State of Managed Runtimes 2013, by Attila Szegedi
 
Using Java from Ruby with JRuby IRB
Using Java from Ruby with JRuby IRBUsing Java from Ruby with JRuby IRB
Using Java from Ruby with JRuby IRB
 
Dockersh and a brief intro to the docker internals
Dockersh and a brief intro to the docker internalsDockersh and a brief intro to the docker internals
Dockersh and a brief intro to the docker internals
 
Hybrid concurrency patterns
Hybrid concurrency patternsHybrid concurrency patterns
Hybrid concurrency patterns
 

Similar to Charles nutter star techconf 2011 - jvm languages

Clojure - An Introduction for Lisp Programmers
Clojure - An Introduction for Lisp ProgrammersClojure - An Introduction for Lisp Programmers
Clojure - An Introduction for Lisp Programmers
elliando dias
 
JRuby - The Best of Java and Ruby
JRuby - The Best of Java and RubyJRuby - The Best of Java and Ruby
JRuby - The Best of Java and Ruby
Evgeny Rahman
 
RubyStack: the easiest way to deploy Ruby on Rails
RubyStack: the easiest way to deploy Ruby on RailsRubyStack: the easiest way to deploy Ruby on Rails
RubyStack: the easiest way to deploy Ruby on Rails
elliando dias
 

Similar to Charles nutter star techconf 2011 - jvm languages (20)

Real-world polyglot programming on the JVM - Ben Summers (ONEIS)
Real-world polyglot programming on the JVM  - Ben Summers (ONEIS)Real-world polyglot programming on the JVM  - Ben Summers (ONEIS)
Real-world polyglot programming on the JVM - Ben Summers (ONEIS)
 
GOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter SlidesGOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter Slides
 
Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?
 
Children of Ruby
Children of RubyChildren of Ruby
Children of Ruby
 
Практики применения JRuby
Практики применения JRubyПрактики применения JRuby
Практики применения JRuby
 
Concurrency and Multithreading Demistified - Reversim Summit 2014
Concurrency and Multithreading Demistified - Reversim Summit 2014Concurrency and Multithreading Demistified - Reversim Summit 2014
Concurrency and Multithreading Demistified - Reversim Summit 2014
 
Scala Introduction
Scala IntroductionScala Introduction
Scala Introduction
 
The Enterprise Strikes Back
The Enterprise Strikes BackThe Enterprise Strikes Back
The Enterprise Strikes Back
 
Clojure - An Introduction for Lisp Programmers
Clojure - An Introduction for Lisp ProgrammersClojure - An Introduction for Lisp Programmers
Clojure - An Introduction for Lisp Programmers
 
JRuby - The Best of Java and Ruby
JRuby - The Best of Java and RubyJRuby - The Best of Java and Ruby
JRuby - The Best of Java and Ruby
 
Introduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platformIntroduction to Kotlin Language and its application to Android platform
Introduction to Kotlin Language and its application to Android platform
 
Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013Beyond JVM - YOW! Brisbane 2013
Beyond JVM - YOW! Brisbane 2013
 
Introduction to JRuby
Introduction to JRubyIntroduction to JRuby
Introduction to JRuby
 
Tampering with JavaScript
Tampering with JavaScriptTampering with JavaScript
Tampering with JavaScript
 
Ruby on the JVM
Ruby on the JVMRuby on the JVM
Ruby on the JVM
 
RubyStack: the easiest way to deploy Ruby on Rails
RubyStack: the easiest way to deploy Ruby on RailsRubyStack: the easiest way to deploy Ruby on Rails
RubyStack: the easiest way to deploy Ruby on Rails
 
IJTC%202009%20JRuby
IJTC%202009%20JRubyIJTC%202009%20JRuby
IJTC%202009%20JRuby
 
IJTC%202009%20JRuby
IJTC%202009%20JRubyIJTC%202009%20JRuby
IJTC%202009%20JRuby
 
The Future of Node - @rvagg - NodeConf Christchurch 2015
The Future of Node - @rvagg - NodeConf Christchurch 2015The Future of Node - @rvagg - NodeConf Christchurch 2015
The Future of Node - @rvagg - NodeConf Christchurch 2015
 
Ruby Midwest 2010 jRuby by Charles Nutter
Ruby Midwest 2010 jRuby by Charles NutterRuby Midwest 2010 jRuby by Charles Nutter
Ruby Midwest 2010 jRuby by Charles Nutter
 

More from StarTech Conference

Mike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to youMike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to you
StarTech Conference
 
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
StarTech Conference
 
Scott Chacon - Cuento de tres árboles
Scott Chacon - Cuento de tres árbolesScott Chacon - Cuento de tres árboles
Scott Chacon - Cuento de tres árboles
StarTech Conference
 
Robert Nyman - HTML5 apis where no man has gone before startechconf
Robert Nyman - HTML5 apis where no man has gone before startechconfRobert Nyman - HTML5 apis where no man has gone before startechconf
Robert Nyman - HTML5 apis where no man has gone before startechconf
StarTech Conference
 
Markos calderon lecciones aprendidas del desarrollo de un sistema de web co...
Markos calderon   lecciones aprendidas del desarrollo de un sistema de web co...Markos calderon   lecciones aprendidas del desarrollo de un sistema de web co...
Markos calderon lecciones aprendidas del desarrollo de un sistema de web co...
StarTech Conference
 
Ravi Mynampaty - developing findability standards
Ravi Mynampaty - developing findability standardsRavi Mynampaty - developing findability standards
Ravi Mynampaty - developing findability standards
StarTech Conference
 
Abraham Barrera - dev-cross-mobile
Abraham Barrera - dev-cross-mobileAbraham Barrera - dev-cross-mobile
Abraham Barrera - dev-cross-mobile
StarTech Conference
 
Eduardo Silva - monkey http-server everywhere
Eduardo Silva - monkey http-server everywhereEduardo Silva - monkey http-server everywhere
Eduardo Silva - monkey http-server everywhere
StarTech Conference
 
Rey Bango - HTML5: polyfills and shims
Rey Bango -  HTML5: polyfills and shimsRey Bango -  HTML5: polyfills and shims
Rey Bango - HTML5: polyfills and shims
StarTech Conference
 
Tom Preston Werner - Optimize for happiness
Tom Preston Werner -  Optimize for happinessTom Preston Werner -  Optimize for happiness
Tom Preston Werner - Optimize for happiness
StarTech Conference
 
Mark ramm To relate or not to relate
Mark ramm   To relate or not to relateMark ramm   To relate or not to relate
Mark ramm To relate or not to relate
StarTech Conference
 

More from StarTech Conference (19)

Mike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to youMike hostetler - jQuery knowledge append to you
Mike hostetler - jQuery knowledge append to you
 
Luis Meijueiro - Open Data
Luis Meijueiro - Open DataLuis Meijueiro - Open Data
Luis Meijueiro - Open Data
 
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
Stephen Anderson - Como construimos e hicimos crecer una empresa de consultor...
 
Jonathan snook - fake-it
Jonathan snook - fake-itJonathan snook - fake-it
Jonathan snook - fake-it
 
Scott Chacon - Cuento de tres árboles
Scott Chacon - Cuento de tres árbolesScott Chacon - Cuento de tres árboles
Scott Chacon - Cuento de tres árboles
 
T.Pollak y C.Yaconi - Prey
T.Pollak y C.Yaconi - PreyT.Pollak y C.Yaconi - Prey
T.Pollak y C.Yaconi - Prey
 
Jano Gonzalez - jruby
Jano Gonzalez - jrubyJano Gonzalez - jruby
Jano Gonzalez - jruby
 
Pedro Fuentes - star techconf
Pedro Fuentes - star techconfPedro Fuentes - star techconf
Pedro Fuentes - star techconf
 
Robert Nyman - HTML5 apis where no man has gone before startechconf
Robert Nyman - HTML5 apis where no man has gone before startechconfRobert Nyman - HTML5 apis where no man has gone before startechconf
Robert Nyman - HTML5 apis where no man has gone before startechconf
 
Markos calderon lecciones aprendidas del desarrollo de un sistema de web co...
Markos calderon   lecciones aprendidas del desarrollo de un sistema de web co...Markos calderon   lecciones aprendidas del desarrollo de un sistema de web co...
Markos calderon lecciones aprendidas del desarrollo de un sistema de web co...
 
Ravi Mynampaty - developing findability standards
Ravi Mynampaty - developing findability standardsRavi Mynampaty - developing findability standards
Ravi Mynampaty - developing findability standards
 
Caridy patino - node-js
Caridy patino - node-jsCaridy patino - node-js
Caridy patino - node-js
 
Abraham Barrera - dev-cross-mobile
Abraham Barrera - dev-cross-mobileAbraham Barrera - dev-cross-mobile
Abraham Barrera - dev-cross-mobile
 
Eduardo Silva - monkey http-server everywhere
Eduardo Silva - monkey http-server everywhereEduardo Silva - monkey http-server everywhere
Eduardo Silva - monkey http-server everywhere
 
Stephanie Rewis - css-startech
Stephanie Rewis -  css-startechStephanie Rewis -  css-startech
Stephanie Rewis - css-startech
 
Rey Bango - HTML5: polyfills and shims
Rey Bango -  HTML5: polyfills and shimsRey Bango -  HTML5: polyfills and shims
Rey Bango - HTML5: polyfills and shims
 
Tom Preston Werner - Optimize for happiness
Tom Preston Werner -  Optimize for happinessTom Preston Werner -  Optimize for happiness
Tom Preston Werner - Optimize for happiness
 
Mark ramm To relate or not to relate
Mark ramm   To relate or not to relateMark ramm   To relate or not to relate
Mark ramm To relate or not to relate
 
Greg rewis move-itsession
Greg rewis move-itsessionGreg rewis move-itsession
Greg rewis move-itsession
 

Recently uploaded

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 

Recently uploaded (20)

08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 

Charles nutter star techconf 2011 - jvm languages

  • 1. Building Languages for the JVM Charles Oliver Nutter
  • 2. Me • Charles Oliver Nutter • JRuby and JVM guy • “headius” on most services • headius@headius.com
  • 3. Who Was I? • Java EE architect • Successfully! • Never wrote a parser • Never wrote a compiler • But I wanted to learn how...
  • 4. You • Java? • Ruby? • Python? • C#? • Other?
  • 5. Why Create Languages? • Nothing is perfect • New problems need new solutions • Language design can be fun • Fame and fortune? • Not really.
  • 6. Why Impl a Language? • To learn it? • Sort of... • To learn the target platform • Definitely! • Fame and fortune? • Well...getting there...
  • 7. Challenges • Community • Platform • Specifications • Resources
  • 8. Community • Investment in status quo • Afraid to stand out • Known quantities • Everything else sucks • Gotta get paid!
  • 9. Platform • Matching language semantics • JVM designed around Java • JVM hides underlying platform • Challenging to use • Not bad...C/++ would be way worse • Community may hate it ;-)
  • 10. Specifications • Incomplete • Ruby had none for years • ...and no complete test suites • Difficult to implement • Low level features • Single-implementation quirks • Hard or impossible to optimize
  • 11. Resources • You gotta eat • Not much money in language work • Some parts are hard • OSS is a necessity
  • 12. Why JVM? • Because I am lazy • Because VMs are *hard* • Because I can’t be awesome at everything
  • 13. Ok, Why Really? • Cross-platform • Libraries • Languages • Memory management • Tools • OSS
  • 14. Cross-platform • OpenJDK: Linux, Windows, Solaris, OS X, xBSD • J9: Linux, zLinux, AS/400, ... • HP: OpenVMS, HP/UX, ... • Dalvik (Android): Linux on ARM, x86
  • 15. Libraries • For any need, a dozen libraries • And a couple of them are good! • Cross-platform • Leading edge
  • 16. Selection of languages • Java • Scala • Clojure • JRuby • Mirah • Jython, Groovy, Fantom, Kotlin, Ceylon, ...
  • 17. Memory management • Best GCs in the world • Fastest object allocation • Safe escape hatches like NIO
  • 18. Tools • Debugging • Profiling • Monitoring • JVM internals
  • 19. Open source? • FOSS reference impl (OpenJDK) • Mostly OSS libraries • Heavy OSS culture • Strong OSS influence in OpenJDK core
  • 21. Ruby on the JVM • All of Ruby’s power and beauty • Solid VM underneath • “Just another JVM language”
  • 22. JVM Language • Full interop with Java • Tricky to do... • Very rewarding for 99% case • VM concerns solved • No need to write a GC • No need to write a JIT • ...oh, but wait...
  • 23. More than a JVM language • Use native code where JDK fails us • Paper over ugly bits like CLASSPATH • Matching Ruby semantics exactly* • Push JVM forward too!
  • 24. Playing with JRuby • Simple IRB demo • JRuby on Rails - see Jano’s talk tomorrow • JRuby performance • PotC (???)
  • 25. How did we do it?
  • 26. JRuby Architecture • Parser • Abstract Syntax Tree (AST) • Intermediate Representation (IR) • Core classes • Compiler
  • 27. Parser • Port of MRI’s Bison grammar • “Jay” parser generator for Java • Hand-written lexer • Nearly as fast as the C version • ...once it gets going
  • 28. system ~/projects/jruby $ jruby -y -e "1 + 1" push state 0 value null reduce tate 0 uncover 0 s rule (1) $$1 : goto from state 0 to 2 push state 2 value null lex tate 2 reading tIDENTIFIER value Token { Value=load, s Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/ jruby/kernel.rb:6} shift from state 2 to 33 push state 33 value Token { Value=load, Position=file:/Users/ headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6} lex tate 33 reading tSTRING_BEG value Token { Value=', s Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/ jruby/kernel.rb:6} reduce tate 33 uncover 2 s rule (487) operation : tIDENTIFIER goto from state 2 to 62 push state 62 value Token { Value=load, Position=file:/Users/ headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6} reduce tate 62 uncover 62 rule (252) $$6 : s
  • 29. system ~/projects/jruby $ jruby -y -e "1 + 1" push state 0 value null reduce tate 0 uncover 0 s rule (1) $$1 : goto from state 0 to 2 push state 2 value null lex tate 2 reading tIDENTIFIER value Token { Value=load, s Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/ jruby/kernel.rb:6} shift from state 2 to 33 You will never need this. push state 33 value Token { Value=load, Position=file:/Users/ headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6} lex tate 33 reading tSTRING_BEG value Token { Value=', s Position=file:/Users/headius/projects/jruby/lib/jruby.jar!/ jruby/kernel.rb:6} reduce tate 33 uncover 2 s rule (487) operation : tIDENTIFIER goto from state 2 to 62 push state 62 value Token { Value=load, Position=file:/Users/ headius/projects/jruby/lib/jruby.jar!/jruby/kernel.rb:6} reduce tate 62 uncover 62 rule (252) $$6 : s
  • 30. public class RubyYaccLexer {     public static final Encoding UTF8_ENCODING = UTF8Encoding.INSTANCE;     public static final Encoding USASCII_ENCODING = USASCIIEncoding.INSTANCE;     public static final Encoding ASCII8BIT_ENCODING = ASCIIEncoding.INSTANCE;          private static ByteList END_MARKER = new ByteList(new byte[] {'_', 'E', 'N', 'D', '_', '_'});     private static ByteList BEGIN_DOC_MARKER = new ByteList(new byte[] {'b', 'e', 'g', 'i', 'n'});     private static ByteList END_DOC_MARKER = new ByteList(new byte[] {'e', 'n', 'd'});     private static final HashMap<String, Keyword> map;     static {         map = new HashMap<String, Keyword>();         map.put("end", Keyword.END);         map.put("else", Keyword.ELSE);         map.put("case", Keyword.CASE);         map.put("ensure", Keyword.ENSURE);         map.put("module", Keyword.MODULE);         map.put("elsif", Keyword.ELSIF);         map.put("def", Keyword.DEF);         map.put("rescue", Keyword.RESCUE);         map.put("not", Keyword.NOT);         map.put("then", Keyword.THEN);         map.put("yield", Keyword.YIELD);         map.put("for", Keyword.FOR);         map.put("self", Keyword.SELF);         map.put("false", Keyword.FALSE);
  • 31.     public enum Keyword {         END ("end", Tokens.kEND, Tokens.kEND, LexState.EXPR_END),         ELSE ("else", Tokens.kELSE, Tokens.kELSE, LexState.EXPR_BEG),         CASE ("case", Tokens.kCASE, Tokens.kCASE, LexState.EXPR_BEG),         ENSURE ("ensure", Tokens.kENSURE, Tokens.kENSURE, LexState.EXPR_BEG),         MODULE ("module", Tokens.kMODULE, Tokens.kMODULE, LexState.EXPR_BEG),         ELSIF ("elsif", Tokens.kELSIF, Tokens.kELSIF, LexState.EXPR_BEG),         DEF ("def", Tokens.kDEF, Tokens.kDEF, LexState.EXPR_FNAME),         RESCUE ("rescue", Tokens.kRESCUE, Tokens.kRESCUE_MOD, LexState.EXPR_MID),         NOT ("not", Tokens.kNOT, Tokens.kNOT, LexState.EXPR_BEG),         THEN ("then", Tokens.kTHEN, Tokens.kTHEN, LexState.EXPR_BEG),         YIELD ("yield", Tokens.kYIELD, Tokens.kYIELD, LexState.EXPR_ARG),         FOR ("for", Tokens.kFOR, Tokens.kFOR, LexState.EXPR_BEG),         SELF ("self", Tokens.kSELF, Tokens.kSELF, LexState.EXPR_END),         FALSE ("false", Tokens.kFALSE, Tokens.kFALSE, LexState.EXPR_END),         RETRY ("retry", Tokens.kRETRY, Tokens.kRETRY, LexState.EXPR_END),         RETURN ("return", Tokens.kRETURN, Tokens.kRETURN, LexState.EXPR_MID),         TRUE ("true", Tokens.kTRUE, Tokens.kTRUE, LexState.EXPR_END),         IF ("if", Tokens.kIF, Tokens.kIF_MOD, LexState.EXPR_BEG),         DEFINED_P ("defined?", Tokens.kDEFINED, Tokens.kDEFINED, LexState.EXPR_ARG),
  • 32.     private int yylex() throws IOException {         int c;         boolean spaceSeen = false;         boolean commandState;                  if (lex_strterm != null) {             int tok = lex_strterm.parseString(this, src);             if (tok == Tokens.tSTRING_END || tok == Tokens.tREGEXP_END) {                 lex_strterm = null;                 setState(LexState.EXPR_END);             }             return tok;         }         commandState = commandStart;         commandStart = false;         loop: for(;;) {             c = src.read();             switch(c) {
  • 33.             case '<':                 return lessThan(spaceSeen);             case '>':                 return greaterThan();             case '"':                 return doubleQuote();             case '`':                 return backtick(commandState);             case ''':                 return singleQuote();             case '?':                 return questionMark();             case '&':                 return ampersand(spaceSeen);             case '|':                 return pipe();             case '+':                 return plus(spaceSeen);
  • 34.     private int lessThan(boolean spaceSeen) throws IOException {         int c = src.read();         if (c == '<' && lex_state != LexState.EXPR_DOT && lex_state != LexState.EXPR_CLASS &&                 !isEND() && (!isARG() || spaceSeen)) {             int tok = hereDocumentIdentifier();                          if (tok != 0) return tok;         }                  determineExpressionState();                  switch (c) {         case '=':             if ((c = src.read()) == '>') {                 yaccValue = new Token("<=>", getPosition());                 return Tokens.tCMP;
  • 35. %% program : {                   lexer.setState(LexState.EXPR_BEG);                   support.initTopLocalVariables();               } top_compstmt {   // ENEBO: Removed !compile_for_eval which probably is to reduce warnings                   if ($2 != null) {                       /* last expression should not be void */                       if ($2 instanceof BlockNode) {                           support.checkUselessStatement($<BlockNod e>2.getLast());                       } else {                           support.checkUselessStatement($2);                       }                   }                   support.getResult().setAST(support.addRootNode($ 2, support.getPosition($2)));               }
  • 36. stmt : kALIAS fitem {                     lexer.setState(LexState.EXPR_FNAME);                 } fitem {                     $$ = support.newAlias($1.getPosition(), $2, $4);                 }                 | kALIAS tGVAR tGVAR {                     $$ = new VAliasNode($1.getPosition(), (String) $2.getValue(), (String) $3.getValue());                 }                 | kALIAS tGVAR tBACK_REF {                     $$ = new VAliasNode($1.getPosition(), (String) $2.getValue(), "$" + $<BackRefNode>3.getType());                 }                 | kALIAS tGVAR tNTH_REF {                     support.yyerror("can't make alias for the number variables");                 }                 | kUNDEF undef_list {                     $$ = $2;                 }                 | stmt kIF_MOD expr_value {                     $$ = new IfNode(support.getPosition($1), support.getConditionNode($3), $1, null);                 }
  • 37.   public Object yyparse (RubyYaccLexer yyLex) throws java.io.IOException {     if (yyMax <= 0) yyMax = 256;"" " // initial size     int yyState = 0, yyStates[] = new int[yyMax];" // state stack     Object yyVal = null, yyVals[] = new Object[yyMax];" // value stack     int yyToken = -1;" " " " " // current input     int yyErrorFlag = 0;" " " " // #tokens to shift     yyLoop: for (int yyTop = 0;; ++ yyTop) {       if (yyTop >= yyStates.length) {" " " // dynamically increase         int[] i = new int[yyStates.length+yyMax];         System.arraycopy(yyStates, 0, i, 0, yyStates.length);         yyStates = i;         Object[] o = new Object[yyVals.length+yyMax];         System.arraycopy(yyVals, 0, o, 0, yyVals.length);         yyVals = o;       }       yyStates[yyTop] = yyState;       yyVals[yyTop] = yyVal;       if (yydebug != null) yydebug.push(yyState, yyVal);
  • 38.         if (state == null) {             yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]);         } else {             yyVal = state.execute(support, lexer, yyVal, yyVals, yyTop);         }
  • 39. states[23] = new ParserState() {   public Object execute(ParserSupport support, RubyYaccLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {                     yyVal = new IfNode(support.getPosition(((Node)yyVals[-2+yyTop])), support.getConditionNode(((Node)yyVals[0+yyTop])), ((Node)yyVals[-2+yyTop]), null);     return yyVal;   } }; Never look at this. states[24] = new ParserState() {   public Object execute(ParserSupport support, RubyYaccLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {                     yyVal = new IfNode(support.getPosition(((Node)yyVals[-2+yyTop])), support.getConditionNode(((Node)yyVals[0+yyTop])), null, ((Node)yyVals[-2+yyTop]));     return yyVal;   } };
  • 40. AST • Interpreted directly • Specialized in places • Large and rich
  • 41. $ ast -e "a = true; if a; 2; else; 3; end" AST: RootNode 0 BlockNode 0 NewlineNode 0 LocalAsgnNode:a 0 TrueNode:true 0 NewlineNode 0 IfNode 0 LocalVarNode:a 0 NewlineNode 0 FixnumNode 0 NewlineNode 0 FixnumNode 0
  • 42. public class IfNode extends Node {     private final Node condition;     private final Node thenBody;     private final Node elseBody;     public IfNode(ISourcePosition position, Node condition, Node thenBody, Node elseBody) {         super(position);                  assert condition != null : "condition is not null"; // assert thenBody != null : "thenBody is not null"; // assert elseBody != null : "elseBody is not null";                  this.condition = condition;         this.thenBody = thenBody;         this.elseBody = elseBody;     }
  • 43.     @Override     public IRubyObject interpret(Ruby runtime, ThreadContext context, IRubyObject self, Block aBlock) {         ISourcePosition position = getPosition();         context.setFile(position.getFile());         context.setLine(position.getStartLine());         IRubyObject result = condition.interpret(runtime, context, self, aBlock);                  if (result.isTrue()) {             return thenBody == null ? runtime.getNil() : thenBody.interpret(runtime, context, self, aBlock);         } else {             return elseBody == null ? runtime.getNil() : elseBody.interpret(runtime, context, self, aBlock);         }     }
  • 44. IR (future work) • Control flow graph • Ruby-specific instruction set • Optimizing compiler • Ruby-level optimizations
  • 45. jruby -e “1 + 1”
  • 46. 2011-11-04T05:23:09.375-03:00: IR_Printer: instrs: 0 %self = recv_self 1 %block(0:0) = recv_closure 2 file_name(-e) 3 line_num(0) 4 %v_0 = call(+, 1:fixnum, [1:fixnum]) 5 return(%v_0) 2011-11-04T05:23:09.375-03:00: IR_Printer: live variables: %v_0: 4-5
  • 47. a = 1; while a < 10; puts a; a += 1; end
  • 48. 2011-11-04T05:25:23.517-03:00: IR_Printer: instrs: 0 %self = recv_self 1 %block(0:0) = recv_closure 2 file_name(-e) 3 line_num(0) 4 a(0:1) = 1:fixnum 5 _LOOP_BEGIN_0: 6 %v_1 = call(<, a(0:1), [10:fixnum]) 7 beq(%v_1, true, _ITER_BEGIN_0) 8 %v_0 = nil 9 jump _LOOP_END_0 10 _ITER_BEGIN_0: 11 %v_2 = call(puts, %self, [a(0:1)]) 12 %v_3 = call(+, a(0:1), [1:fixnum]) 13 a(0:1) = copy(%v_3) 14 %v_0 = copy(%v_3) 15 thread_poll 16 _ITER_END_0: 17 jump _LOOP_BEGIN_0 18 _LOOP_END_0: 19 return(%v_0) 2011-11-04T05:25:23.517-03:00: IR_Printer: live variables: %v_0: 8-19 %v_1: 6-7 %v_3: 12-14
  • 49. 2011-11-04T05:25:23.518-03:00: IRScope: ################## After CFG Linearize################## 2011-11-04T05:25:23.518-03:00: IR_Printer: ---------------------------------------- 2011-11-04T05:25:23.518-03:00: IR_Printer: Method [root]: [script]:-e 2011-11-04T05:25:23.518-03:00: IR_Printer: Graph: BB [4:LBL_3]:>[7], <[3] BB [1:LBL_1]:>[8,2] BB [2:LBL_2]:>[3], <[1] BB [7:_LOOP_END_0]:>[8], <[4] BB [8:LBL_4]:<[1,7] BB [3:_LOOP_BEGIN_0]:>[5,4], <[6,2] BB [6:_ITER_END_0]:>[3], <[5] BB [5:_ITER_BEGIN_0]:>[6], <[3]
  • 50. 2011-11-04T05:25:23.517-03:00: IR_Printer: instrs: 0 %self = recv_self 1 %block(0:0) = recv_closure 2 file_name(-e) 3 line_num(0) 4 a(0:1) = 1:fixnum 5 _LOOP_BEGIN_0: 6 %v_1 = call(<, a(0:1), [10:fixnum]) 7 beq(%v_1, true, _ITER_BEGIN_0) 8 %v_0 = nil 9 jump _LOOP_END_0 10 _ITER_BEGIN_0: 11 %v_2 = call(puts, %self, [a(0:1)]) 12 %v_3 = call(+, a(0:1), [1:fixnum]) 13 a(0:1) = copy(%v_3) 14 %v_0 = copy(%v_3) 15 thread_poll 16 _ITER_END_0: 17 jump _LOOP_BEGIN_0 18 _LOOP_END_0: 19 return(%v_0) 2011-11-04T05:25:23.517-03:00: IR_Printer: live variables: %v_0: 8-19 %v_1: 6-7 %v_3: 12-14
  • 51. 2011-11-04T05:25:23.518-03:00: IR_Printer: Instructions: BB [4:LBL_3] %v_0 = nil BB [1:LBL_1] BB [2:LBL_2] %self = recv_self %block(0:0) = recv_closure file_name(-e) line_num(0) a(0:1) = 1:fixnum BB [7:_LOOP_END_0] return(%v_0) BB [8:LBL_4] return(nil)
  • 52. Core classes • Mostly Java-based • Leverage JDK where possible • Work around JDK where necessary • Use Ruby when possible
  • 53. @JRubyClass(name="Fixnum", parent="Integer", include="Precision") public class RubyFixnum extends RubyInteger {          public static RubyClass createFixnumClass(Ruby runtime) {         RubyClass fixnum = runtime.defineClass("Fixnum", runtime.getInteger(),                 ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  • 54.     @JRubyMethod(name = "+")     public IRubyObject op_plus(ThreadContext context, IRubyObject other) {         if (other instanceof RubyFixnum) {             return addFixnum(context, (RubyFixnum)other);         }         return addOther(context, other);     }
  • 55.     private IRubyObject addFixnum(ThreadContext context, RubyFixnum other) {         long otherValue = other.value;         long result = value + otherValue;         if (additionOverflowed(value, otherValue, result)) {             return addAsBignum(context, other);         }         return newFixnum(context.getRuntime(), result);     }
  • 56. Compiler • AST-walking • ASM bytecode library • Minimal optimizations • Invokedynamic really helps • IR offers new opportunities
  • 57. jruby --bytecode -e “1 + 1”
  • 58. ALOAD 0 INVOKEVIRTUAL ruby/__dash_e__.getCallSite0 ALOAD 1 ALOAD 2 ALOAD 1 GETFIELD org/jruby/runtime/ThreadContext.runtime INVOKESTATIC org/jruby/RubyFixnum.one LDC 1 INVOKEVIRTUAL org/jruby/runtime/CallSite.call ARETURN
  • 59. public class ASTCompiler {     private boolean isAtRoot = true;     public void compileBody(Node node, BodyCompiler context, boolean expr) {         Node oldBodyNode = currentBodyNode;         currentBodyNode = node;         compile(node, context, expr);         currentBodyNode = oldBodyNode;     }          public void compile(Node node, BodyCompiler context, boolean expr) {         if (node == null) {             if (expr) context.loadNil();             return;         }         switch (node.getNodeType()) {             case ALIASNODE:                 compileAlias((AliasNode) node, context, expr);                 break;             case ANDNODE:                 compileAnd(node, context, expr);                 break;
  • 60.     public void compileIf(Node node, BodyCompiler context, final boolean expr) {         final IfNode ifNode = (IfNode) node;         // optimizations if we know ahead of time it will always be true or false         Node actualCondition = ifNode.getCondition();         while (actualCondition instanceof NewlineNode) {             actualCondition = ((NewlineNode)actualCondition).getNextNode();         }         if (actualCondition.getNodeType().alwaysTrue()) {             // compile condition as non-expr and just compile "then" body             compile(actualCondition, context, false);             compile(ifNode.getThenBody(), context, expr);         } else if (actualCondition.getNodeType().alwaysFalse()) {             // always false or nil             compile(ifNode.getElseBody(), context, expr);         } else {
  • 61.             BranchCallback trueCallback = new BranchCallback() {                 public void branch(BodyCompiler context) {                     if (ifNode.getThenBody() != null) {                         compile(ifNode.getThenBody(), context, expr);                     } else {                         if (expr) context.loadNil();                     }                 }             };             BranchCallback falseCallback = new BranchCallback() {                 public void branch(BodyCompiler context) {                     if (ifNode.getElseBody() != null) {                         compile(ifNode.getElseBody(), context, expr);                     } else {                         if (expr) context.loadNil();                     }                 }             };                          // normal             compile(actualCondition, context, true);             context.performBooleanBranch(trueCallback, falseCallback);         }
  • 62. public abstract class BaseBodyCompiler implements BodyCompiler {     protected SkinnyMethodAdapter method;     protected VariableCompiler variableCompiler;     protected InvocationCompiler invocationCompiler;     protected int argParamCount;     protected Label[] currentLoopLabels;     protected Label scopeStart = new Label();     protected Label scopeEnd = new Label();     protected Label redoJump;     protected boolean inNestedMethod = false;     private int lastLine = -1;     private int lastPositionLine = -1;     protected StaticScope scope;     protected ASTInspector inspector;     protected String methodName;     protected String rubyName;     protected StandardASMCompiler script;
  • 63.     public void performBooleanBranch(BranchCallback trueBranch, BranchCallback falseBranch) {         Label afterJmp = new Label();         Label falseJmp = new Label();         // call isTrue on the result         isTrue();         method.ifeq(falseJmp); // EQ == 0 (i.e. false)         trueBranch.branch(this);         method.go_to(afterJmp);         // FIXME: optimize for cases where we have no false branch         method.label(falseJmp);         falseBranch.branch(this);         method.label(afterJmp);     }
  • 64. More Demos! • JRuby + JVM flags • JRuby concurrency • Invokedynamic (Java 7) • Redcar Editor • Ruboto (Android) • VisualVM
  • 65. Gracias! • Charles Oliver Nutter • “headius” on most services • headius@headius.com

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n