0
class Foo{
    private final Object obj0;
    private final Object obj1;
    private final Object def;

    Public Foo(obj0,obj1,def){
        this.obj0 = obj0;
        this.obj1 = obj1;
        this.def = def;
    }

    public int foo(long v){
        if      (v==0) return obj0;
        else if (v==1) return obj1;
        else return def;
    }
}

I now want to redefine method Foo.foo and add a case at runtime:

class Foo{
    private final Object obj0;
    private final Object obj1;
    private final Object def;

    Public Foo(obj0,obj1,def){
        this.obj0 = obj0;
        this.obj1 = obj1;
        this.def = def;
    }

    public int foo(long v){
        if      (v==0) return obj0;
        else if (v==1) return obj1;
        else if (v==2) return obj2_fixed_when_added;
        else return def;
    }
}

Now conceptually this sounds pretty easy. In pseudo code:

visit method Foo.foo{
    boolean transformed = false;
    for(stmt : Foo.foo.body){
        if (transformed || stmt is not if){
            emit unchanged
        }else if (stmt is IF){
            visit if-clause
            visit else-clause
            emit IF
        }else{
            emit new ELSIF for case v==2
            emit stmt unchanged
            transformed = true;
        }
    }
}

Problem then is that we're working with bytecode and the AST doesn't really deal with statements, let alone IF abstractions.

So what I can do in this case is use the fact that all the guards are mutually exclusive and each case returns on its own and just redefine the method like

    public int foo(long v){
        if      (v==2) return obj2_fixed_when_added;
        if      (v==0) return obj0;
        else if (v==1) return obj1;
        else return def;
    }

which is basically

visit method Foo.foo{
    emit new IF for v==2
    emit rest of method body unchanged
}

Crude, but does the job, I guess, and we can now reload the class to make the changes available.

The problem then becomes the

return obj2_fixed_when_added;

If I can just create a new Instance of whatever obj2_fixed_when_added is, I'd do so.

However, what do I do if I can not and want to set it to one specific Object the transformer generates as he inserts the new IF?

User1291
  • 7,664
  • 8
  • 51
  • 108
  • I'd use a wrapper rather than playing with bytecode. Or then, generate a wrapper class and use that object rather than the declared/expected one. – Luiggi Mendoza Nov 22 '17 at 13:43
  • @LuiggiMendoza believe me, I'd rather not be "playing with bytecode", myself, but it's what I'm required to do, so ... – User1291 Nov 22 '17 at 13:46
  • Yeah, and frameworks like Spring does it as well and inject the instance of a proxy class rather than the instance of the original class. – Luiggi Mendoza Nov 22 '17 at 13:47
  • Can you explain why this needs to be done at the bytecode level? Perhaps consider the Strategy pattern, where a class can be injected into `Foo` which knows how to handle various situations. – Andrew S Nov 22 '17 at 13:48
  • @LuiggiMendoza how does this work exactly? I mean, even if I were to create a proxy class, I'd still need to somehow get the object from my transformer to the proxy? – User1291 Nov 22 '17 at 13:49
  • @AndrewS needs to be done at bytecode level because it triggers a series of class redefinitions which change the way an access to a field of ``Foo`` performs. The decision how the new access should be executed is done at runtime and we need to hardcode the new access pattern for performance reasons. – User1291 Nov 22 '17 at 13:52
  • What does _hardcode_ mean? Can there be various implementations and decide at runtime which to use? – Andrew S Nov 22 '17 at 14:24
  • @AndrewS Basically, we're deciding at runtime if it makes sense to move fields to another class. *Hardcode* means, we want to write the necessary offsets for an access with ``sun.misc.Unsafe`` into the code directly, rather than having to look them up somewhere. – User1291 Nov 22 '17 at 14:39
  • I doubt that using `sun.misc.Unsafe` will improve the performance compared to ordinary field access. Neither will introducing even more conditionals. Besides that, what kind of Instrumentation are we talking about, load time or redefinition of already loaded classes? For HotSpot, changing fields is not supported when redefining. The possibilities to pass objects depend on the surrounding conditions. – Holger Nov 22 '17 at 22:01

0 Answers0