Safe Serialization Under Mutual Suspicion/"Reversing" Evaluation

From Erights

(Difference between revisions)
Jump to: navigation, search
(continuing wikification)
(removing spans)
Line 119: Line 119:
<code><pre>
<code><pre>
-
        <span class="keyword">def</span> <span class="defobj">makeUnevaler</span>(<span class="defvar">uncallerList</span>, <span class="defvar">unscopeMap</span>) :near {
+
def makeUnevaler(uncallerList, unscopeMap) :near {
-
     <span class="keyword">def</span> <span class="defobj">unevaler</span> {
+
     def unevaler {
-
         <span class="keyword">to</span> <span class="defverb">recognize</span>(<span class="defvar">root</span>, <span class="defvar">builder</span>) :(<span class="keyword">def</span> <span class="defvar">Root</span> := builder.getRootType()) {
+
         to recognize(root, builder) :(def Root := builder.getRootType()) {
-
             <span class="keyword">def</span> <span class="defvar">Node</span> := builder.getNodeType()
+
             def Node := builder.getNodeType()
-
             <span class="keyword">def</span> <span class="defvar">uncallers</span> := uncallerList.snapshot()
+
             def uncallers := uncallerList.snapshot()
-
             <span class="keyword">def</span> <span class="defvar">unscope</span> := unscopeMap.diverge()
+
             def unscope := unscopeMap.diverge()
-
             <span class="comment"># forward declaration </span>
+
             # forward declaration  
-
             <span class="keyword">def</span> <span class="defvar">generate</span>
+
             def generate
-
             <span class="comment">/** traverse an uncall portrayal */</span>
+
             /** traverse an uncall portrayal */
-
             <span class="keyword">def</span> <span class="defobj">genCall</span>(<span class="defvar">rec</span>, <span class="defvar">verb</span> :String, <span class="defvar">args</span> :any[]) :Node {
+
             def genCall(rec, verb :String, args :any[]) :Node {
-
                 <span class="keyword">def</span> <span class="defvar">recExpr</span> := generate(rec)
+
                 def recExpr := generate(rec)
-
                 <span class="keyword">var</span> <span class="defvar">argExprs</span> := []
+
                 var argExprs := []
-
                 <span class="keyword">for</span> <span class="defvar">arg</span> in args {
+
                 for arg in args {
                     argExprs := argExprs.with(generate(arg))
                     argExprs := argExprs.with(generate(arg))
                 }
                 }
Line 142: Line 142:
             }
             }
-
             <span class="comment">/** When we're past all the variable manipulation. */</span>
+
             /** When we're past all the variable manipulation. */
-
             <span class="keyword">def</span> <span class="defobj">genObject</span>(<span class="defvar">obj</span>) :Node {
+
             def genObject(obj) :Node {
</pre></code>
</pre></code>

Revision as of 01:03, 30 January 2008

As we've seen, we make serializers, unserializers, and other transformers like expression simplifiers by composing a recognizer with a builder. The interface between the two is the DEBuilder API, explained in Appendix A: The Data-E Manual. Since most of the API is a straightforward reflection of the Data-E grammar productions, if you wish, you may safely skip these details and proceed here by example.

Evaluating Data-E

The semantics of Data-E are defined by the semantics of its evaluation as an E program. We could unserialize using the full E evaluator. However, this is inefficient both as an implementation and as an explanation. Instead, here is the Data-E evaluator as a builder, implementing exactly this subset of E's semantics.

pragma.syntax("0.8")

def deSubgraphKit {
    to makeBuilder(scope) :near {

        # The index of the next temp variable

        var nextTemp := 0

        # The frame of temp variables
        def temps := [].diverge()

        # The type returned by "internal" productions and passed as arguments to represent

        # built subtrees.
        def Node := any

        # The type returned by the builder as a whole.
        def Root := any

        # DEBuilderOf is a parameterized type constructor.

        def deSubgraphBuilder implements DEBuilderOf(Node, Root) {
            to getNodeType() :near { Node }
            to getRootType() :near { Root }

            /** Called at the end with the reconstructed root to obtain the value to return. */
            to buildRoot(root :Node)        :Root  { root }

            /** A literal evaluates to its value. */
            to buildLiteral(value)          :Node  { value }

            /** A free variable's name is looked up in the scope. */
            to buildImport(varName :String) :Node  { scope[varName] }

            /** A temporary variable's index is looked up in the temps frame. */
            to buildIbid(tempIndex :int)    :Node  { temps[tempIndex] }

            /** Perform the  described call. */
            to buildCall(rec :Node, verb :String, args :Node[]) :Node {
                # E.call(..) is E's reflective invocation construct. For example, E.call(2, "add", [3])
                # performs the same call as 2.add(3).
                <u>E.call(rec, verb, args)</u>
            }

            /**
             * Called prior to building the right-hand side of a defexpr, to allocate and bind the
             * next two temp variables to a promise and its resolver.

             * 
             * @return the index of the temp holding the promise. The temp holding the
             *               resolver is understood to be this plus one.
             */
            to buildPromise() :int {
                def promIndex := nextTemp
                nextTemp += 2
                def [prom,res] := Ref.promise()
                temps[promIndex] := prom
                temps[promIndex+1] := res
                promIndex
            }

            /**
             * Called once the right-hand side of a defexpr is built, use the resolver to resolve
             * the value of the promise.
             * 
             * @return the value of the right-hand side.
             */

            to buildDefrec(resIndex :int, rValue :Node) :Node {
                temps[resIndex].resolve(rValue)
                rValue
            }

            # ... buildDefine is an optimization of buildDefrec for known non-cyclic cases.
        }
    }
    # ... other useful tools 

}

As we see, the E.call(..) underlined above is where all the object construction is done. All the rest is plumbing to hook the up the references among these objects.

The only extra parameter to the above code, in addition to those specified by the DEBuilder API, is the scope parameter to makeBuilder(..). Typically, we will express unserialization-time policy choices using only this hook. With a bit of pre-planning at serialization time, this can be a surprisingly powerful hook, and will often prove adequate.

Unevaluating to Data-E

Because the keys of a unscope table may be arbitrary values, including unresolved promises, it needs to be the special kind of map called a CycleBreaker. For present purposes, we can ignore this issue.

We are now ready for the heart of serialization -- the Data-E subgraph recognizer. It has two parameters for expressing policy -- the uncallerList and the unscopeMap.

Since we are evaling "in reverse", we need the inverse of a scope, which we call an unscope. An unscope maps from arbitrary values to a description of the "variable name" presumed to hold that reference. In the unscope table passed in as unscopeMap, each description is a normal variable name string, as would be used to look the value up in a scope. On each recognize(..), the ".diverge()" makes a private copy of the unscopeMap we put in the variable unscope, which we use from there. This private unscope table gets additional mappings from values to integers representing temporary variable indices.

The uncallerList is used to obtain a portrayal of each object, as we explain below.

def makeUnevaler(uncallerList, unscopeMap) :near {
    def unevaler {
        to recognize(root, builder) :(def Root := builder.getRootType()) {

            def Node := builder.getNodeType()

            def uncallers := uncallerList.snapshot()
            def unscope := unscopeMap.diverge()

            # forward declaration 

            def generate

            /** traverse an uncall portrayal */
            def genCall(rec, verb :String, args :any[]) :Node {
                def recExpr := generate(rec)
                var argExprs := []
                for arg in args {
                    argExprs := argExprs.with(generate(arg))
                }
                builder.buildCall(recExpr, verb, argExprs)
            }

            /** When we're past all the variable manipulation. */

            def genObject(obj) :Node {


This is part wikified from the original Part 2: "Reversing" Evaluation

Personal tools
more tools