Skip to content

Commit fd7331a

Browse files
text/template: revert CL 66410 "add break, continue actions in ranges"
The new break and continue actions do not work in html/template, and fixing them requires thinking about security issues that seem too tricky at this stage of the release. We will try again for 1.11. Original CL description: text/template: add break, continue actions in ranges Adds the two range control actions "break" and "continue". They act the same as the Go keywords break and continue, but are simplified in that only the innermost range statement can be broken out of or continued. Fixes #20531 Updates #20531 Updates #23683 Change-Id: Ia7fd3c409163e3bcb5dc42947ae90b15bdf89853 Reviewed-on: https://go-review.googlesource.com/92155 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent f54f780 commit fd7331a

File tree

10 files changed

+31
-225
lines changed

10 files changed

+31
-225
lines changed

api/go1.10.txt

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -618,24 +618,6 @@ pkg syscall (windows-386), func CreateProcessAsUser(Token, *uint16, *uint16, *Se
618618
pkg syscall (windows-386), type SysProcAttr struct, Token Token
619619
pkg syscall (windows-amd64), func CreateProcessAsUser(Token, *uint16, *uint16, *SecurityAttributes, *SecurityAttributes, bool, uint32, *uint16, *uint16, *StartupInfo, *ProcessInformation) error
620620
pkg syscall (windows-amd64), type SysProcAttr struct, Token Token
621-
pkg text/template/parse, const NodeBreak = 20
622-
pkg text/template/parse, const NodeBreak NodeType
623-
pkg text/template/parse, const NodeContinue = 21
624-
pkg text/template/parse, const NodeContinue NodeType
625-
pkg text/template/parse, method (*BreakNode) Copy() Node
626-
pkg text/template/parse, method (*BreakNode) Position() Pos
627-
pkg text/template/parse, method (*BreakNode) String() string
628-
pkg text/template/parse, method (*BreakNode) Type() NodeType
629-
pkg text/template/parse, method (*ContinueNode) Copy() Node
630-
pkg text/template/parse, method (*ContinueNode) Position() Pos
631-
pkg text/template/parse, method (*ContinueNode) String() string
632-
pkg text/template/parse, method (*ContinueNode) Type() NodeType
633-
pkg text/template/parse, type BreakNode struct
634-
pkg text/template/parse, type BreakNode struct, embedded NodeType
635-
pkg text/template/parse, type BreakNode struct, embedded Pos
636-
pkg text/template/parse, type ContinueNode struct
637-
pkg text/template/parse, type ContinueNode struct, embedded NodeType
638-
pkg text/template/parse, type ContinueNode struct, embedded Pos
639621
pkg time, func LoadLocationFromTZData(string, []uint8) (*Location, error)
640622
pkg unicode, const Version = "10.0.0"
641623
pkg unicode, var Masaram_Gondi *RangeTable

doc/go1.10.html

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,11 +1069,6 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
10691069
<dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt>
10701070
<dd>
10711071
<p>
1072-
The new actions <code>{{"{{break}}"}}</code> and <code>{{"{{continue}}"}}</code>
1073-
break out of the innermost <code>{{"{{range"}}</code>&nbsp;...<code>}}</code> loop,
1074-
like the corresponding Go statements.
1075-
</p>
1076-
<p>
10771072
The new <a href="/pkg/html/template#Srcset"><code>Srcset</code></a> content
10781073
type allows for proper handling of values within the
10791074
<a href="https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset"><code>srcset</code></a>
@@ -1411,15 +1406,6 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
14111406
</p>
14121407
</dl>
14131408

1414-
<dl id="text/template"><dt><a href="/pkg/text/template/">text/template</a></dt>
1415-
<dd>
1416-
<p>
1417-
The new actions <code>{{"{{break}}"}}</code> and <code>{{"{{continue}}"}}</code>
1418-
break out of the innermost <code>{{"{{range"}}</code>&nbsp;...<code>}}</code> loop,
1419-
like the corresponding Go statements.
1420-
</p>
1421-
</dl>
1422-
14231409
<dl id="time"><dt><a href="/pkg/time/">time</a></dt>
14241410
<dd>
14251411
<p>

src/text/template/doc.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,6 @@ data, defined in detail in the corresponding sections that follow.
110110
T0 is executed; otherwise, dot is set to the successive elements
111111
of the array, slice, or map and T1 is executed.
112112
113-
{{break}}
114-
Break out of the surrounding range loop.
115-
116-
{{continue}}
117-
Begin the next iteration of the surrounding range loop.
118-
119113
{{template "name"}}
120114
The template with the specified name is executed with nil data.
121115

src/text/template/exec.go

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ const maxExecDepth = 100000
2525
// template so that multiple executions of the same template
2626
// can execute in parallel.
2727
type state struct {
28-
tmpl *Template
29-
wr io.Writer
30-
node parse.Node // current node, for errors.
31-
vars []variable // push-down stack of variable values.
32-
depth int // the height of the stack of executing templates.
33-
rangeDepth int // nesting level of range loops.
28+
tmpl *Template
29+
wr io.Writer
30+
node parse.Node // current node, for errors
31+
vars []variable // push-down stack of variable values.
32+
depth int // the height of the stack of executing templates.
3433
}
3534

3635
// variable holds the dynamic value of a variable such as $, $x etc.
@@ -221,17 +220,9 @@ func (t *Template) DefinedTemplates() string {
221220
return s
222221
}
223222

224-
type rangeControl int8
225-
226-
const (
227-
rangeNone rangeControl = iota // no action.
228-
rangeBreak // break out of range.
229-
rangeContinue // continues next range iteration.
230-
)
231-
232223
// Walk functions step through the major pieces of the template structure,
233224
// generating output as they go.
234-
func (s *state) walk(dot reflect.Value, node parse.Node) rangeControl {
225+
func (s *state) walk(dot reflect.Value, node parse.Node) {
235226
s.at(node)
236227
switch node := node.(type) {
237228
case *parse.ActionNode:
@@ -242,42 +233,29 @@ func (s *state) walk(dot reflect.Value, node parse.Node) rangeControl {
242233
s.printValue(node, val)
243234
}
244235
case *parse.IfNode:
245-
return s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
236+
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
246237
case *parse.ListNode:
247238
for _, node := range node.Nodes {
248-
if c := s.walk(dot, node); c != rangeNone {
249-
return c
250-
}
239+
s.walk(dot, node)
251240
}
252241
case *parse.RangeNode:
253-
return s.walkRange(dot, node)
242+
s.walkRange(dot, node)
254243
case *parse.TemplateNode:
255244
s.walkTemplate(dot, node)
256245
case *parse.TextNode:
257246
if _, err := s.wr.Write(node.Text); err != nil {
258247
s.writeError(err)
259248
}
260249
case *parse.WithNode:
261-
return s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
262-
case *parse.BreakNode:
263-
if s.rangeDepth == 0 {
264-
s.errorf("invalid break outside of range")
265-
}
266-
return rangeBreak
267-
case *parse.ContinueNode:
268-
if s.rangeDepth == 0 {
269-
s.errorf("invalid continue outside of range")
270-
}
271-
return rangeContinue
250+
s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
272251
default:
273252
s.errorf("unknown node: %s", node)
274253
}
275-
return rangeNone
276254
}
277255

278256
// walkIfOrWith walks an 'if' or 'with' node. The two control structures
279257
// are identical in behavior except that 'with' sets dot.
280-
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) rangeControl {
258+
func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
281259
defer s.pop(s.mark())
282260
val := s.evalPipeline(dot, pipe)
283261
truth, ok := isTrue(val)
@@ -286,14 +264,13 @@ func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.
286264
}
287265
if truth {
288266
if typ == parse.NodeWith {
289-
return s.walk(val, list)
267+
s.walk(val, list)
290268
} else {
291-
return s.walk(dot, list)
269+
s.walk(dot, list)
292270
}
293271
} else if elseList != nil {
294-
return s.walk(dot, elseList)
272+
s.walk(dot, elseList)
295273
}
296-
return rangeNone
297274
}
298275

299276
// IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
@@ -331,14 +308,13 @@ func isTrue(val reflect.Value) (truth, ok bool) {
331308
return truth, true
332309
}
333310

334-
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
311+
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
335312
s.at(r)
336313
defer s.pop(s.mark())
337314
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
338315
// mark top of stack before any variables in the body are pushed.
339316
mark := s.mark()
340-
s.rangeDepth++
341-
oneIteration := func(index, elem reflect.Value) rangeControl {
317+
oneIteration := func(index, elem reflect.Value) {
342318
// Set top var (lexically the second if there are two) to the element.
343319
if len(r.Pipe.Decl) > 0 {
344320
s.setVar(1, elem)
@@ -347,33 +323,26 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
347323
if len(r.Pipe.Decl) > 1 {
348324
s.setVar(2, index)
349325
}
350-
ctrl := s.walk(elem, r.List)
326+
s.walk(elem, r.List)
351327
s.pop(mark)
352-
return ctrl
353328
}
354329
switch val.Kind() {
355330
case reflect.Array, reflect.Slice:
356331
if val.Len() == 0 {
357332
break
358333
}
359334
for i := 0; i < val.Len(); i++ {
360-
if ctrl := oneIteration(reflect.ValueOf(i), val.Index(i)); ctrl == rangeBreak {
361-
break
362-
}
335+
oneIteration(reflect.ValueOf(i), val.Index(i))
363336
}
364-
s.rangeDepth--
365-
return rangeNone
337+
return
366338
case reflect.Map:
367339
if val.Len() == 0 {
368340
break
369341
}
370342
for _, key := range sortKeys(val.MapKeys()) {
371-
if ctrl := oneIteration(key, val.MapIndex(key)); ctrl == rangeBreak {
372-
break
373-
}
343+
oneIteration(key, val.MapIndex(key))
374344
}
375-
s.rangeDepth--
376-
return rangeNone
345+
return
377346
case reflect.Chan:
378347
if val.IsNil() {
379348
break
@@ -384,25 +353,20 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) rangeControl {
384353
if !ok {
385354
break
386355
}
387-
if ctrl := oneIteration(reflect.ValueOf(i), elem); ctrl == rangeBreak {
388-
break
389-
}
356+
oneIteration(reflect.ValueOf(i), elem)
390357
}
391358
if i == 0 {
392359
break
393360
}
394-
s.rangeDepth--
395-
return rangeNone
361+
return
396362
case reflect.Invalid:
397363
break // An invalid value is likely a nil map, etc. and acts like an empty map.
398364
default:
399365
s.errorf("range can't iterate over %v", val)
400366
}
401-
s.rangeDepth--
402367
if r.ElseList != nil {
403-
return s.walk(dot, r.ElseList)
368+
s.walk(dot, r.ElseList)
404369
}
405-
return rangeNone
406370
}
407371

408372
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {

src/text/template/exec_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,6 @@ var execTests = []execTest{
513513
{"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
514514
{"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
515515
{"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
516-
{"range quick break", `{{range .SI}}{{break}}{{.}}{{end}}`, "", tVal, true},
517-
{"range break after two", `{{range $i, $x := .SI}}{{if ge $i 2}}{{break}}{{end}}{{.}}{{end}}`, "34", tVal, true},
518-
{"range continue", `{{range .SI}}{{continue}}{{.}}{{end}}`, "", tVal, true},
519-
{"range continue condition", `{{range .SI}}{{if eq . 3 }}{{continue}}{{end}}{{.}}{{end}}`, "45", tVal, true},
520516

521517
// Cute examples.
522518
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},

src/text/template/parse/lex.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ const (
6060
// Keywords appear after all the rest.
6161
itemKeyword // used only to delimit the keywords
6262
itemBlock // block keyword
63-
itemBreak // break keyword
64-
itemContinue // continue keyword
6563
itemDot // the cursor, spelled '.'
6664
itemDefine // define keyword
6765
itemElse // else keyword
@@ -76,8 +74,6 @@ const (
7674
var key = map[string]itemType{
7775
".": itemDot,
7876
"block": itemBlock,
79-
"break": itemBreak,
80-
"continue": itemContinue,
8177
"define": itemDefine,
8278
"else": itemElse,
8379
"end": itemEnd,

src/text/template/parse/lex_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ var lexTests = []lexTest{
192192
tRight,
193193
tEOF,
194194
}},
195-
{"keywords", "{{range if else end with break continue}}", []item{
195+
{"keywords", "{{range if else end with}}", []item{
196196
tLeft,
197197
mkItem(itemRange, "range"),
198198
tSpace,
@@ -203,10 +203,6 @@ var lexTests = []lexTest{
203203
mkItem(itemEnd, "end"),
204204
tSpace,
205205
mkItem(itemWith, "with"),
206-
tSpace,
207-
mkItem(itemBreak, "break"),
208-
tSpace,
209-
mkItem(itemContinue, "continue"),
210206
tRight,
211207
tEOF,
212208
}},

src/text/template/parse/node.go

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ const (
6969
NodeTemplate // A template invocation action.
7070
NodeVariable // A $ variable.
7171
NodeWith // A with action.
72-
NodeBreak // A break action.
73-
NodeContinue // A continue action.
7472
)
7573

7674
// Nodes.
@@ -798,68 +796,6 @@ func (r *RangeNode) Copy() Node {
798796
return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
799797
}
800798

801-
// BreakNode represents a {{break}} action.
802-
type BreakNode struct {
803-
NodeType
804-
Pos
805-
tr *Tree
806-
}
807-
808-
func (t *Tree) newBreak(pos Pos) *BreakNode {
809-
return &BreakNode{NodeType: NodeBreak, Pos: pos, tr: t}
810-
}
811-
812-
func (b *BreakNode) Type() NodeType {
813-
return b.NodeType
814-
}
815-
816-
func (b *BreakNode) String() string {
817-
return "{{break}}"
818-
}
819-
820-
func (b *BreakNode) Copy() Node {
821-
return b.tr.newBreak(b.Pos)
822-
}
823-
824-
func (b *BreakNode) Position() Pos {
825-
return b.Pos
826-
}
827-
828-
func (b *BreakNode) tree() *Tree {
829-
return b.tr
830-
}
831-
832-
// ContinueNode represents a {{continue}} action.
833-
type ContinueNode struct {
834-
NodeType
835-
Pos
836-
tr *Tree
837-
}
838-
839-
func (t *Tree) newContinue(pos Pos) *ContinueNode {
840-
return &ContinueNode{NodeType: NodeContinue, Pos: pos, tr: t}
841-
}
842-
843-
func (c *ContinueNode) Type() NodeType {
844-
return c.NodeType
845-
}
846-
847-
func (c *ContinueNode) String() string {
848-
return "{{continue}}"
849-
}
850-
851-
func (c *ContinueNode) Copy() Node {
852-
return c.tr.newContinue(c.Pos)
853-
}
854-
855-
func (c *ContinueNode) Position() Pos {
856-
return c.Pos
857-
}
858-
859-
func (c *ContinueNode) tree() *Tree {
860-
return c.tr
861-
}
862-
863799
// WithNode represents a {{with}} action and its commands.
864800
type WithNode struct {
865801
BranchNode

0 commit comments

Comments
 (0)