@@ -11,6 +11,7 @@ import (
11
11
"os"
12
12
"path"
13
13
"path/filepath"
14
+ "reflect"
14
15
"sync"
15
16
"text/template"
16
17
"text/template/parse"
@@ -26,7 +27,9 @@ type Template struct {
26
27
// template's in sync.
27
28
text * template.Template
28
29
// The underlying template's parse tree, updated to be HTML-safe.
29
- Tree * parse.Tree
30
+ Tree * parse.Tree
31
+ // The original functions, before wrapping.
32
+ funcMap FuncMap
30
33
* nameSpace // common to all associated templates
31
34
}
32
35
@@ -35,7 +38,7 @@ var escapeOK = fmt.Errorf("template escaped correctly")
35
38
36
39
// nameSpace is the data structure shared by all templates in an association.
37
40
type nameSpace struct {
38
- mu sync.Mutex
41
+ mu sync.RWMutex
39
42
set map [string ]* Template
40
43
escaped bool
41
44
esc escaper
@@ -45,8 +48,8 @@ type nameSpace struct {
45
48
// itself.
46
49
func (t * Template ) Templates () []* Template {
47
50
ns := t .nameSpace
48
- ns .mu .Lock ()
49
- defer ns .mu .Unlock ()
51
+ ns .mu .RLock ()
52
+ defer ns .mu .RUnlock ()
50
53
// Return a slice so we don't expose the map.
51
54
m := make ([]* Template , 0 , len (ns .set ))
52
55
for _ , v := range ns .set {
@@ -84,8 +87,8 @@ func (t *Template) checkCanParse() error {
84
87
if t == nil {
85
88
return nil
86
89
}
87
- t .nameSpace .mu .Lock ()
88
- defer t .nameSpace .mu .Unlock ()
90
+ t .nameSpace .mu .RLock ()
91
+ defer t .nameSpace .mu .RUnlock ()
89
92
if t .nameSpace .escaped {
90
93
return fmt .Errorf ("html/template: cannot Parse after Execute" )
91
94
}
@@ -94,6 +97,16 @@ func (t *Template) checkCanParse() error {
94
97
95
98
// escape escapes all associated templates.
96
99
func (t * Template ) escape () error {
100
+ t .nameSpace .mu .RLock ()
101
+ escapeErr := t .escapeErr
102
+ t .nameSpace .mu .RUnlock ()
103
+ if escapeErr != nil {
104
+ if escapeErr == escapeOK {
105
+ return nil
106
+ }
107
+ return escapeErr
108
+ }
109
+
97
110
t .nameSpace .mu .Lock ()
98
111
defer t .nameSpace .mu .Unlock ()
99
112
t .nameSpace .escaped = true
@@ -121,6 +134,8 @@ func (t *Template) Execute(wr io.Writer, data interface{}) error {
121
134
if err := t .escape (); err != nil {
122
135
return err
123
136
}
137
+ t .nameSpace .mu .RLock ()
138
+ defer t .nameSpace .mu .RUnlock ()
124
139
return t .text .Execute (wr , data )
125
140
}
126
141
@@ -136,20 +151,36 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
136
151
if err != nil {
137
152
return err
138
153
}
154
+ t .nameSpace .mu .RLock ()
155
+ defer t .nameSpace .mu .RUnlock ()
139
156
return tmpl .text .Execute (wr , data )
140
157
}
141
158
142
159
// lookupAndEscapeTemplate guarantees that the template with the given name
143
160
// is escaped, or returns an error if it cannot be. It returns the named
144
161
// template.
145
162
func (t * Template ) lookupAndEscapeTemplate (name string ) (tmpl * Template , err error ) {
146
- t .nameSpace .mu .Lock ()
147
- defer t .nameSpace .mu .Unlock ()
148
- t .nameSpace .escaped = true
163
+ t .nameSpace .mu .RLock ()
149
164
tmpl = t .set [name ]
165
+ var escapeErr error
166
+ if tmpl != nil {
167
+ escapeErr = tmpl .escapeErr
168
+ }
169
+ t .nameSpace .mu .RUnlock ()
170
+
150
171
if tmpl == nil {
151
172
return nil , fmt .Errorf ("html/template: %q is undefined" , name )
152
173
}
174
+ if escapeErr != nil {
175
+ if escapeErr != escapeOK {
176
+ return nil , escapeErr
177
+ }
178
+ return tmpl , nil
179
+ }
180
+
181
+ t .nameSpace .mu .Lock ()
182
+ defer t .nameSpace .mu .Unlock ()
183
+ t .nameSpace .escaped = true
153
184
if tmpl .escapeErr != nil && tmpl .escapeErr != escapeOK {
154
185
return nil , tmpl .escapeErr
155
186
}
@@ -229,6 +260,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error
229
260
nil ,
230
261
text ,
231
262
text .Tree ,
263
+ nil ,
232
264
t .nameSpace ,
233
265
}
234
266
t .set [name ] = ret
@@ -259,8 +291,10 @@ func (t *Template) Clone() (*Template, error) {
259
291
nil ,
260
292
textClone ,
261
293
textClone .Tree ,
294
+ t .funcMap ,
262
295
ns ,
263
296
}
297
+ ret .wrapFuncs ()
264
298
ret .set [ret .Name ()] = ret
265
299
for _ , x := range textClone .Templates () {
266
300
name := x .Name ()
@@ -269,12 +303,15 @@ func (t *Template) Clone() (*Template, error) {
269
303
return nil , fmt .Errorf ("html/template: cannot Clone %q after it has executed" , t .Name ())
270
304
}
271
305
x .Tree = x .Tree .Copy ()
272
- ret . set [ name ] = & Template {
306
+ tc : = & Template {
273
307
nil ,
274
308
x ,
275
309
x .Tree ,
310
+ src .funcMap ,
276
311
ret .nameSpace ,
277
312
}
313
+ tc .wrapFuncs ()
314
+ ret .set [name ] = tc
278
315
}
279
316
// Return the template associated with the name of this template.
280
317
return ret .set [ret .Name ()], nil
@@ -288,6 +325,7 @@ func New(name string) *Template {
288
325
nil ,
289
326
template .New (name ),
290
327
nil ,
328
+ nil ,
291
329
ns ,
292
330
}
293
331
tmpl .set [name ] = tmpl
@@ -313,6 +351,7 @@ func (t *Template) new(name string) *Template {
313
351
nil ,
314
352
t .text .New (name ),
315
353
nil ,
354
+ nil ,
316
355
t .nameSpace ,
317
356
}
318
357
if existing , ok := tmpl .set [name ]; ok {
@@ -343,10 +382,35 @@ type FuncMap map[string]interface{}
343
382
// type. However, it is legal to overwrite elements of the map. The return
344
383
// value is the template, so calls can be chained.
345
384
func (t * Template ) Funcs (funcMap FuncMap ) * Template {
346
- t .text .Funcs (template .FuncMap (funcMap ))
385
+ t .funcMap = funcMap
386
+ t .wrapFuncs ()
347
387
return t
348
388
}
349
389
390
+ // wrapFuncs records the functions with text/template. We wrap them to
391
+ // unlock the nameSpace. See TestRecursiveExecute for a test case.
392
+ func (t * Template ) wrapFuncs () {
393
+ if len (t .funcMap ) == 0 {
394
+ return
395
+ }
396
+ tfuncs := make (template.FuncMap , len (t .funcMap ))
397
+ for name , fn := range t .funcMap {
398
+ fnv := reflect .ValueOf (fn )
399
+ wrapper := func (args []reflect.Value ) []reflect.Value {
400
+ t .nameSpace .mu .RUnlock ()
401
+ defer t .nameSpace .mu .RLock ()
402
+ if fnv .Type ().IsVariadic () {
403
+ return fnv .CallSlice (args )
404
+ } else {
405
+ return fnv .Call (args )
406
+ }
407
+ }
408
+ wrapped := reflect .MakeFunc (fnv .Type (), wrapper )
409
+ tfuncs [name ] = wrapped .Interface ()
410
+ }
411
+ t .text .Funcs (tfuncs )
412
+ }
413
+
350
414
// Delims sets the action delimiters to the specified strings, to be used in
351
415
// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
352
416
// definitions will inherit the settings. An empty delimiter stands for the
@@ -360,8 +424,8 @@ func (t *Template) Delims(left, right string) *Template {
360
424
// Lookup returns the template with the given name that is associated with t,
361
425
// or nil if there is no such template.
362
426
func (t * Template ) Lookup (name string ) * Template {
363
- t .nameSpace .mu .Lock ()
364
- defer t .nameSpace .mu .Unlock ()
427
+ t .nameSpace .mu .RLock ()
428
+ defer t .nameSpace .mu .RUnlock ()
365
429
return t .set [name ]
366
430
}
367
431
0 commit comments