mirror of
https://github.com/golang/go
synced 2024-11-25 07:17:56 -07:00
exp/template/html: avoid redundant escaping directives.
This is a possible optimization. I'm not sure the complexity is worth it. The new benchmark in escape_test is 46us without and 35us with the optimization. R=nigeltao CC=golang-dev https://golang.org/cl/5168041
This commit is contained in:
parent
585294db73
commit
b0cddb98b9
@ -262,19 +262,54 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
|
||||
i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq)
|
||||
if i != -1 {
|
||||
for _, name := range s[:i] {
|
||||
newCmds = append(newCmds, newIdentCmd(name))
|
||||
newCmds = appendCmd(newCmds, newIdentCmd(name))
|
||||
}
|
||||
s = s[i+1:]
|
||||
}
|
||||
newCmds = append(newCmds, id)
|
||||
newCmds = appendCmd(newCmds, id)
|
||||
}
|
||||
// Create any remaining sanitizers.
|
||||
for _, name := range s {
|
||||
newCmds = append(newCmds, newIdentCmd(name))
|
||||
newCmds = appendCmd(newCmds, newIdentCmd(name))
|
||||
}
|
||||
p.Cmds = newCmds
|
||||
}
|
||||
|
||||
// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
|
||||
// for all x.
|
||||
var redundantFuncs = map[string]map[string]bool{
|
||||
"exp_template_html_commentescaper": {
|
||||
"exp_template_html_attrescaper": true,
|
||||
"exp_template_html_nospaceescaper": true,
|
||||
"exp_template_html_htmlescaper": true,
|
||||
},
|
||||
"exp_template_html_cssescaper": {
|
||||
"exp_template_html_attrescaper": true,
|
||||
},
|
||||
"exp_template_html_jsregexpescaper": {
|
||||
"exp_template_html_attrescaper": true,
|
||||
},
|
||||
"exp_template_html_jsstrescaper": {
|
||||
"exp_template_html_attrescaper": true,
|
||||
},
|
||||
"exp_template_html_urlescaper": {
|
||||
"exp_template_html_urlnormalizer": true,
|
||||
},
|
||||
}
|
||||
|
||||
// appendCmd appends the given command to the end of the command pipeline
|
||||
// unless it is redundant with the last command.
|
||||
func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode {
|
||||
if n := len(cmds); n != 0 {
|
||||
last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode)
|
||||
next, _ := cmd.Args[0].(*parse.IdentifierNode)
|
||||
if ok && redundantFuncs[last.Ident][next.Ident] {
|
||||
return cmds
|
||||
}
|
||||
}
|
||||
return append(cmds, cmd)
|
||||
}
|
||||
|
||||
// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found.
|
||||
func indexOfStr(s string, strs []string, eq func(a, b string) bool) int {
|
||||
for i, t := range strs {
|
||||
|
@ -1577,3 +1577,47 @@ func TestEscapeSetErrorsNotIgnorable(t *testing.T) {
|
||||
defer expectExecuteFailure(t, &b)
|
||||
s.Execute(&b, "t", nil)
|
||||
}
|
||||
|
||||
func TestRedundantFuncs(t *testing.T) {
|
||||
inputs := []interface{}{
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
|
||||
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
||||
` !"#$%&'()*+,-./` +
|
||||
`0123456789:;<=>?` +
|
||||
`@ABCDEFGHIJKLMNO` +
|
||||
`PQRSTUVWXYZ[\]^_` +
|
||||
"`abcdefghijklmno" +
|
||||
"pqrstuvwxyz{|}~\x7f" +
|
||||
"\u00A0\u0100\u2028\u2029\ufeff\ufdec\ufffd\uffff\U0001D11E" +
|
||||
"&%22\\",
|
||||
CSS(`a[href =~ "//example.com"]#foo`),
|
||||
HTML(`Hello, <b>World</b> &tc!`),
|
||||
HTMLAttr(` dir="ltr"`),
|
||||
JS(`c && alert("Hello, World!");`),
|
||||
JSStr(`Hello, World & O'Reilly\x21`),
|
||||
URL(`greeting=H%69&addressee=(World)`),
|
||||
}
|
||||
|
||||
for n0, m := range redundantFuncs {
|
||||
f0 := funcMap[n0].(func(...interface{}) string)
|
||||
for n1, _ := range m {
|
||||
f1 := funcMap[n1].(func(...interface{}) string)
|
||||
for _, input := range inputs {
|
||||
want := f0(input)
|
||||
if got := f1(want); want != got {
|
||||
t.Errorf("%s %s with %T %q: want\n\t%q,\ngot\n\t%q", n0, n1, input, input, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEscapedExecute(b *testing.B) {
|
||||
tmpl := template.Must(Escape(template.Must(template.New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`))))
|
||||
var buf bytes.Buffer
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tmpl.Execute(&buf, "foo & 'bar' & baz")
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user