Native Queries (under the hood)
[ bamboo ] 11:14, Thursday, 17 November 2005

We are having a discussion about NativeQueries on castleproject-devel and I decided to wrap up the discussion here by describing some of the details and shared goodies of our implementation.

> Can I assume that you're using the new GetMethodIL (or something like that)?

We are using the Mono.Cecil API not the reflection API.

Simply because Cecil's version of GetMethodBody is so much better and it already worked on most platforms we support. One of our first contributions was to get Cecil running on the Compact Framework. After some conditional compilation fun (among other things) Cecil now works on mono, ms.net 1.1, ms.net 2.0, cf 1.0 and cf 2.0. Pretty cool.

> This is just freaking cool. And the idea is awesome.

Yeah, we think so too! (:

> If you cache the > execution plans, I suppose you can get a very good performance out of it. >

Currently we are not even caching the execution plans and the performance it's pretty close to the raw query execution performance one would get from the underlying query api.

But, yes, caching the execution plans (or transformed expression trees in our case) is the next logical step.

> This is a whole different way of thinking about how to mess with the > code...! > I've only heard about Cecil until now, wasn't aware that it was operational > on this level. Are there are docs yet, or is it read the code to find out? >

The Cecil object model is pretty self documenting.

The code we are contributing to cecil is a little more involved but still easy to follow if you stick to the public API.

It basically allows you to get high level control flow graphs out of method definitions. These high level cfgs can also contain decompiled expression trees (an expression tree is a structure pretty similar to a bound boo ast).

Here's a simple but complete example that uses Cecil and our contributed code (Cecil.FlowAnalysis) to dump the body of a method:

import System
import Mono.Cecil
import Cecil.FlowAnalysis
import Cecil.FlowAnalysis.ActionFlow
import Cecil.FlowAnalysis.CodeStructure

class Address:
    [property(State)]
    _state as string
    
class Customer:
    [property(Address)]
    _address as Address
    
class Predicate:
    def Match(c as Customer):
        return c.Address.State == "SP"

def GetMatchMethod():
    asm = AssemblyFactory.GetAssembly(typeof(Predicate).Module.FullyQualifiedName)
    return asm.MainModule.Types["Predicate"].Methods.GetMethod("Match")[0]

cfg = FlowGraphFactory.CreateControlFlowGraph(GetMatchMethod())
// a cfg is at the IL instruction level it is good for
// low level IL optimization and simple analysis

afg = FlowGraphFactory.CreateActionFlowGraph(cfg)
// an afg is at the statement/expression level
// good for code analysis

// print the interesting blocks
for block in afg.Blocks:
    expression as IExpression = null
    
    if block isa IReturnActionBlock:
        expression = (block as IReturnActionBlock).Expression
        Console.Write("return ")
    elif block isa IAssignActionBlock:
        expression = (block as IAssignActionBlock).AssignExpression
        
    continue if expression is null
    
    // expression is an ast like expression tree
    // which supports visitors
    // it is more like a bound ast (with all references
    // pointing to the right Mono.Cecil entities)
    //

    // ExpressionPrinter is a handy visitor
    print ExpressionPrinter.ToString(expression)

The code above will print:

    local0 = string.op_Equality(c.get_Address().get_State(), "SP")
    return local0
Going from that to a high performant query native to the underlying engine is a fun ride! TrackBack
Comments
Post a comment









Remember personal info?