My favorites | Sign in
Google
                
Search
for
Updated Apr 15, 2009 by jgw+personal@google.com
EnumOptimizations  
Optimizations for generated enum code

Introduction

Java's enum semantics require each value to essentially be an anonymous subclass of its enum class. The following example:

  enum Foo {
    FOO_0("0"), FOO_1("1") {
      public String getMessage() {
        return "something different";
      }
    }, FOO_2("2");

    private final String msg;

    Foo(String msg) {
      this.msg = msg;
    }

    public String getMessage() {
      return msg;
    }
  }

gives some idea of why it's done this way. The GWT compiler currently generates the code for enums following these semantics quite literally, which can lead to some fairly significant bloat. In this case, the initialization for the Foo enum values alone looks like this:

  FOO_0 = $Hello$Foo(new Hello$Foo(), 'FOO_0', 0, '0');
  FOO_1 = $Hello$Foo$1(new Hello$Foo$1(), 'FOO_1', 1, '1');
  FOO_2 = $Hello$Foo(new Hello$Foo(), 'FOO_2', 2, '2');

along with a class definition for Foo like this:

function $Hello$Foo(this$static, enum$name, enum$ordinal, msg){
  $clinit_6();
  this$static.ordinal = enum$ordinal;
  this$static.msg = msg;
  return this$static;
}

function getMessage_0(){
  return this.msg;
}

function Hello$Foo(){
}

_ = Hello$Foo.prototype = new Enum();
_.getMessage = getMessage_0;
_.typeId$ = 0;
_.msg = null;

and a class definition for Foo$1 like this:

function $Hello$Foo$1(this$static, enum$name, enum$ordinal, $anonymous0){
  $clinit_5();
  this$static.ordinal = enum$ordinal;
  this$static.msg = $anonymous0;
  return this$static;
}

function getMessage(){
  return 'something different';
}

function Hello$Foo$1(){
}

_ = Hello$Foo$1.prototype = new Hello$Foo();
_.getMessage = getMessage;
_.typeId$ = 0;

Improvements

Clearly it's a lot smaller when obfuscated, but there's plenty of room for improvement here. It's probably true that most enums, most of the time, are semantically equivalent to their ordinals (i.e., they just add a bit of type safety on top of using integers). Also, it's not possible to extend enums, so the number of cases where the generated code has to deal with virtual dispatch is somewhat limited.

Giant TODO: How can we make all this stuff simpler and not break the general case? Ideally, we would compile all enums down to just integers, but virtual dispatch would seem to make this somewhat complicated.

Open Questions and Ideas

Performance Impact

Any optimizations that make generated enum code smaller seem likely to be a net performance win, but we'll have to keep a close eye on them to be sure.


Comment by mark.renouf, May 26, 2009

toString() returns the same as name() by default

Comment by jgw+personal@google.com, May 26, 2009

That's unfortunate, isn't it? Sounds like we're going to have to take the latter approach if we want to eliminate all those (usually) unnecessary strings.

Comment by cromwellian, Jun 25, 2009
Joel,
wouldn't this transformation work?
foo_Names = ["FOO_0", "FOO_1", "FOO_2"];
fooFuncs = { getMessage: [getMessage1, getMessage1, getMessage2] };

Enum's then become integer ordinals. FooEnumType.name() would return foo_Names[ordinal()] FooEnumType.func() would invoke fooFuncs[obfuscatedFuncName][ordinal](args).

The problem is, by the time you reach the JS AST, you've lost any idea that these are enums. So it would have to be done at the JDT->Java AST stage I think.

This is a special case of object inlining where you know that a given type can only be instantiated with N different permutations, all field values statically known at compile time. In which case, you can rip all of the object's fields out to global singleton and replace them with a pointer (integer or other reference)

Comment by joelgwebber, Jun 26, 2009

I've been mocking up a proposal very much like this, actually. I guess that's a good sign.

The biggest complication is that enums extend Object, and can implement interfaces. And because we can't always tighten the types in practice, you need virtual dispatch on Number (!) or some other compiler magic.

I'll update this article sometime soon with what I've come up with so far. It's a bit complex, but I'm fairly certain it will work, and let us just use integers as you've proposed.


Sign in to add a comment