InfraRuby Vision delivers solutions for mobile and web applications.
You can try InfraRuby live in your browser!
Try the InfraRuby statically typed Ruby compiler live in your browser. Try our examples or write your own code.
Follow @InfraRuby on Twitter for news and updates.

How to compile Ruby case statements

In this post you will see how the InfraRuby statically typed Ruby compiler translates Ruby case statements to Java bytecode.

the usual case

Consider this method, which takes one argument and invokes some other method depending on the value of that argument:

## Integer -> void
def f(n)
	case n
	when 0
		f0
	when 1
		f1
	else
		fn
	end
	return
end

For the case statement, Ruby interpreters invoke the === operator (also known as “threequals”) for each when clause until a match is found. That code is equivalent to this code:

## Integer -> void
def f(n)
	if 0 === n
		f0
	elsif 1 === n
		f1
	else
		fn
	end
	return
end

The compiler emits conditional branch instructions using the result of the === operator (named operator$eqv internally):

  public void f(ruby.Integer)
       0: aload_1
       1: astore_2
       2: getstatic     ruby/main$const.x0:Lruby/Integer;
       5: aload_2
       6: invokevirtual ruby/Integer.operator$eqv:(Ljava/lang/Object;)Z
       9: ifeq          19
      12: aload_0
      13: invokevirtual f0:()V
      16: goto          40
      19: getstatic     ruby/main$const.x1:Lruby/Integer;
      22: aload_2
      23: invokevirtual ruby/Integer.operator$eqv:(Ljava/lang/Object;)Z
      26: ifeq          36
      29: aload_0
      30: invokevirtual f1:()V
      33: goto          40
      36: aload_0
      37: invokevirtual fn:()V
      40: return

the primitive case

InfraRuby supports primitive types boolean, byte, char, int16, int32, int64, float32 and float64.

You can use primitive values in Ruby interpreters with our compatibility package: the infraruby-java gem. This package is available under the MIT license. To install this package, type in a shell:

gem install infraruby-java

For case statements with primitive values, the compiler emits conditional branch, lookupswitch or tableswitch instructions, as appropriate.

using the lookupswitch instruction

Consider this method, which takes a primitive int32 argument:

## int32 -> void
def f(n)
	case n
	when 0.i32
		f0
	when 1.i32
		f1
	when 7.i32
		f7
	else
		fn
	end
	return
end

As the values for the when clauses are sparse, the compiler emits a lookupswitch instruction:

  public void f(int)
       0: iload_1
       1: lookupswitch {
                     0: 36
                     1: 43
                     7: 50
               default: 57
          }
      36: aload_0
      37: invokevirtual f0:()V
      40: goto          61
      43: aload_0
      44: invokevirtual f1:()V
      47: goto          61
      50: aload_0
      51: invokevirtual f7:()V
      54: goto          61
      57: aload_0
      58: invokevirtual fn:()V
      61: return
using the tableswitch instruction

Consider this method, which takes a primitive int32 argument:

## int32 -> void
def f(n)
	case n
	when 0.i32
		f0
	when 1.i32
		f1
	when 2.i32
		f2
	else
		fn
	end
	return
end

As the values for the when clauses are not sparse, the compiler emits a tableswitch instruction:

  public void f(int)
       0: iload_1
       1: tableswitch {
                     0: 28
                     1: 35
                     2: 42
               default: 49
          }
      28: aload_0
      29: invokevirtual f0:()V
      32: goto          53
      35: aload_0
      36: invokevirtual f1:()V
      39: goto          53
      42: aload_0
      43: invokevirtual f2:()V
      46: goto          53
      49: aload_0
      50: invokevirtual fn:()V
      53: return

the String case

For case statements with String values the compiler emits instructions to walk the trie constructed from the values of the when clauses.

Consider this method, which takes a String argument:

## String -> void
def f(s)
	case s
	when "x"
		fx
	when "y"
		fy
	when "z"
		fz
	else
		fs
	end
	return
end

The compiler emits conditional branch, lookupswitch or tableswitch instructions, as appropriate:

  public void f(ruby.String)
       0: aload_1
       1: invokevirtual ruby/String.to_java_bytes_unsafe:()[B
       4: astore_2
       5: aload_2
       6: arraylength
       7: iconst_1
       8: if_icmpne     61
      11: aload_2
      12: iconst_0
      13: baload
      14: tableswitch {
                   120: 40
                   121: 47
                   122: 54
               default: 61
          }
      40: aload_0
      41: invokevirtual fx:()V
      44: goto          65
      47: aload_0
      48: invokevirtual fy:()V
      51: goto          65
      54: aload_0
      55: invokevirtual fz:()V
      58: goto          65
      61: aload_0
      62: invokevirtual fs:()V
      65: return

Try the InfraRuby statically typed Ruby compiler live in your browser. You can use InfraRuby for your own projects with our free download!

Follow @InfraRuby on Twitter
Copyright © 2011-2017 InfraRuby Vision Limited