#57 Mixin support

brian Tue 21 Mar 2006

I checked in initial support for mixins.

The fcode format is enhanced slightly:

typeMeta
{
  u2 self  (typeRefs.def)
  u2 base  (typeRefs.def or 0xFFFF)
  u2 mixinCount
  u2 mixin[mixinCount] (typeRefs.def)
  u4 flags
}

There are two new opcodes for invoking a mixin method:

"CallMixinStatic  2  (method)  // call static mixin method",
"CallMixinVirtual 2  (method)  // call virtual mixin method",

The best way to understand my Java implementation is via a simple example. Consider this Fan code:

class Play mixin Foo
{
  static Void main()
  {
    echo(s)
    echo(make.i)
  } 
}

mixin Foo
{
  static Str s() { return "static" }
  Str i() { return "instance" }
}

Compiles to the the following fcode:

cmdLineScript::Play extends sys::Obj
  mixin cmdLineScript::Foo

  sys::Void main() [public static]
    [Code]
      0: CallMixinStatic  cmdLineScript::Foo.s() -> sys::Str
      3: CallStatic       sys::Obj.echo(sys::Obj) -> sys::Void
      6: CallNew          cmdLineScript::Play.make() -> sys::Void
      9: CallMixinVirtual cmdLineScript::Foo.i() -> sys::Str
     12: CallStatic       sys::Obj.echo(sys::Obj) -> sys::Void
     15: ReturnVoid

  sys::Void make() [public new]
    [Code]
      0: ReturnVoid

cmdLineScript::Foo extends null

  sys::Str s() [public static]
    [Code]
      0: LoadStr          static
      3: ReturnObj

  sys::Str i() [public]
    [Code]
      0: LoadStr          instance
      3: ReturnObj

Notice the use of CallMixinStatic and CallMixinVirtual. This basically emits to the following Java:

class Play implements Foo
{
  Str i() { return Foo$.i(this); }
  static void main()
  {
    echo( make().i() )
    echo( Foo$.s() }
  }
}

interface Foo
{
  Str i()
}

class Foo$
{
  static Str i(Foo self) { return "instance" }
  static Str s() { return "static" }
}

Mixins can have static methods, non-virtual methods, virtual methods, or abstract methods. I don't have some of the sophisticated error checking we discussed (such that if there is ambiguity in what should be inherited you have to explicitly override the method).

They basically work right now, but there are a couple tasks I have left to do:

  • Finishing tying mixins into type checking
  • Mixins parameter defaults
  • Mixin reflection
  • Mixins with natives?

brian Sat 25 Mar 2006

I added support for mixin type checking. A key design decision I made was that mixins act like they inherit from Obj. This means you can use a variable typed as a mixin where an Obj is expected. Likewise you can call Obj methods like type() or toStr() on a variable typed as mixin.

Of course the JVM doesn't like that all since it sees an interface type on the stack which doesn't actually implement Obj (since Obj is a class not an interface). So I fixed this at the compiler level by inserting an Obj cast where ever an mixin (interface) was being used as an Obj.

In Fan reflection it means mixins look like they extend Obj and implement all it's methods:

class Play mixin Foo
{
  static Void main()
  {
    f := Foo.type;
    echo("f:                " + f)
    echo("f.base:           " + f.base)
    echo("f.inheritance:    " + f.inheritance)
    echo("f.fits(Obj.type): " + f.fits(Obj.type))
    echo("f.methods:        " + f.methods)
    echo("f.foo:            " + make.foo)
  }  
}

mixin Foo 
{ 
  Str foo() { return toStr }
}

Outputs:

f:                cmdLineScript::Foo
f.base:           sys::Obj
f.inheritance:    sys::Type[sys::Obj, cmdLineScript::Foo]
f.fits(Obj.type): true
f.methods:        sys::Method[sys::Obj.equals, sys::Obj.toStr, ... cmdLineScript::Foo.foo]
f.foo:            fan.cmdLineScript.Play@1b10d42

brian Sun 26 Mar 2006

I wrapped up the mixin feature with support for default parameters. I also flushed out the test suite and fixed a couple other obscure bugs. I think mixins are basically done for now. I still need to add a bunch of error checking to the compiler - but I'm going to wait until for rewrite for that.

brian Mon 5 Jun 2006

I really want to add field support to mixins - so far I've wanted that everytime I've used a mixin. But it's a pretty big feature so I want to wait and only do it once in the Fan version of the compiler.

In the meantime I added a feature which is almost just as good. A field may be used to "implement" an abstract method which matches its getter:

mixin Foo
{
  abstract Str name()
}

class Bar mixin Foo
{
  Str name := "unnamed"
}

The compiler will stay happy and not complain because although Bar.name is a field, it results in a getter method that will satisfy the contract of Foo.name. Same principle applies if Foo is an abstract class.

brian Sat 24 Jun 2006

I added support for static fields to mixins. I created four new opcodes:

LoadMixinInstance  2 (field) // load field on mixin from storage
StoreMixinInstance 2 (field) // store field on mixin to storage
LoadMixinStatic    2 (field) // load static on mixin field from storage
StoreMixinStatic   2 (field) // store static on mixin field to storage

The instance field accessor opcodes aren't used yet.

Login or Signup to reply.