// escapeTemplateBody escapes the given template assuming the given output // context, and returns the best guess at the output context and whether the // assumption was correct. func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) { filter := func(e1 *escaper, c1 context) bool { if c1.state == stateError { // Do not update the input escaper, e. return false } if !e1.called[t.Name()] { // If t is not recursively called, then c1 is an // accurate output context. return true } // c1 is accurate if it matches our assumed output context. return c.eq(c1) } // We need to assume an output context so that recursive template calls // take the fast path out of escapeTree instead of infinitely recursing. // Naively assuming that the input context is the same as the output // works >90% of the time. e.output[t.Name()] = c return e.escapeListConditionally(c, t.Tree.Root, filter) }
// computeOutCtx takes a template and its start context and computes the output // context while storing any inferences in e. func (e *escaper) computeOutCtx(c context, t *template.Template) context { // Propagate context over the body. c1, ok := e.escapeTemplateBody(c, t) if !ok { // Look for a fixed point by assuming c1 as the output context. if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 { c1, ok = c2, true } // Use c1 as the error context if neither assumption worked. } if !ok && c1.state != stateError { return context{ state: stateError, err: errorf(ErrOutputContext, t.Tree.Root, 0, "cannot compute output context for template %s", t.Name()), } } return c1 }