Friday, February 27, 2009

Hybrid call site model based on Groovy

I'm trying to model a class structure that support the following features:
  • callsite; separating call and execution semantics to
    • support before/after advice of AspectJ's pointcut-advice model (possible support around advice)
    • further support my typing concerns.
  • support statically resolved callsite (resolving is done at compile-time, but also changeable at runtime)
  • support dynamically resolved callsite (as done in Groovy, JRuby, etc.)
  • must be compatible with Java. i.e., Generated classes can be used as normal Java classes.
My below POC is based on Alex T. implementation. But instead of showing all kind of callsite, I show you here only statically resolved callsite. This callsite is surely fast (I hope it's gonna be at the same level of Java) because it's compile-time thing. The interesting point is that, I have a special class loader for these callsites, namely StaticCallSiteClassLoader. What does it do?  This class loader allows re-definition of these callsites.

What I have observed so far is that All of my callsite classes (_0_println, _1_ctor, _2_render and _3_index) have not been loaded when there is no "direct" reference to them. So they can be fully lazy loading using the StaticCallSiteClassLoader. (I've checked this with java --verbose)

How could these callsites are changed at runtime?
You may see the acallsite, an CallSite array that hold all callsites in the example class. It's intended to be public and can be accessed by, for example, a virtual meta-class.
So what's a virtual meta-class? It's kind of meta-class, but in this case, it's not for controlling behaviour of the class (because all dispatch are static). It's for changing behaviour of callsite by reassigning new callsites object to the callsite array.

Notic that I use WeakReference to hold a callsite class loader. This will be making the class loader GC-able. That's it, after deferenceing all old callsites out of the callsite array, the class loader can be unloaded. Therefore, all callsite class definition are gone as well. Then, we can create a new set of callsites for the current class. This technique would be flexible enough for:
  • changing a static call to be a dynamic call
  • changing a dynamic call back to a static call
  • installing a woven callsite with before, after advice (it features dynamic AOP)
  • inserting type advice, which can lead to bytecode inlining
  • design a hybrid type system to support both static and dynamic typing in the same system.
import java.lang.ref.WeakReference;

import runtime.CallSite;
import classloader.StaticCallSiteClassLoader;
import dm.Render;

public class TestController {

    /* synthetic */
    private static WeakReference<StaticCallSiteClassLoader> cl;

    /* synthetic normal callsite */
    public static class _0_println extends CallSite {...}

    /* synthetic constructor */
    public static class _1_ctor extends CallSite {...}

    /* synthetic inter-type declaration callsite. statically resolved */
    public static class _2_render extends CallSite {...}

    /* synthetic property callsite */
    public static class _3_index extends CallSite {...}

    /* synthetic */
    public static CallSite[] acallsite;

    static {
        cl = new WeakReference<StaticCallSiteClassLoader>(
                    new StaticCallSiteClassLoader(TestController.class)
        );
        acallsite = new CallSite[4];
        acallsite[0] = cl.get().get("_0_println");
        acallsite[1] = cl.get().get("_1_ctor");
        acallsite[2] = cl.get().get("_2_render");
        acallsite[3] = cl.get().get("_3_index");
    }

    public TestController(){
        super();
    }

    public int getIndex() {
        return 10;
    }

    public static void main(String[] args) {
        acallsite[0].callStatic("hello world");
        TestController t = (TestController)(acallsite[1].callConstructor());
        acallsite[2].call(t, "hello world");
        acallsite[0].callStatic(acallsite[3].callGetProperty(t, "index"));
    }

}


No comments: