Skip to content

Commit

Permalink
soyhtml: don't write "__all" variable to user's maps.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob Figueiredo committed Sep 30, 2014
1 parent d409380 commit 991318f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 20 deletions.
4 changes: 2 additions & 2 deletions soyhtml/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,9 @@ func (s *state) evalCall(node *ast.CallNode) {
s.errorf("In 'call' command %q, the data reference %q does not resolve to a map.",
node.String(), node.Data.String())
}
callData = scope{result}
callData = newScope(result)
} else {
callData = scope{make(data.Map)}
callData = newScope(make(data.Map))
}

// resolve the params
Expand Down
21 changes: 19 additions & 2 deletions soyhtml/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"log"
"reflect"
"strings"
"testing"

Expand Down Expand Up @@ -595,7 +596,8 @@ func TestLet(t *testing.T) {
// Ensure that the input data map is not updated.
// Ensure that let variables are not passed with data="all"
func TestLetScopes(t *testing.T) {
var m = data.Map{"a": data.Int(1)}
var m = data.Map{"a": data.Int(1), "z": data.Map{"y": data.Int(9)}}
var mcopy = data.Map{"a": data.Int(1), "z": data.Map{"y": data.Int(9)}}
runExecTests(t, []execTest{
{"letscopes", "test.main", `{namespace test}
/** @param a */
Expand Down Expand Up @@ -651,9 +653,24 @@ func TestLetScopes(t *testing.T) {
{$a}
{/template}
`, "121314325226", m, true},

{"no-overwrite-map", "test.main", `{namespace test}
/** @param z */
{template .main}
{call .inner data="$z"/}
{/template}
/** @param y */
{template .inner}
{let $a: 8/}
{$y} {$a}
{let $y: 7/}
{sp}{$y}
{/template}
`, "9 8 7", m, true},
})

if len(m) != 1 || m["a"].(data.Int) != 1 {
if !reflect.DeepEqual(m, mcopy) {
t.Errorf("input data map changed: %v", m)
}
}
Expand Down
6 changes: 1 addition & 5 deletions soyhtml/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ func (t Renderer) Execute(wr io.Writer, obj data.Map) (err error) {
autoescapeMode = ast.AutoescapeOn
}

var initialData = make(data.Map)
for k, v := range obj {
initialData[k] = v
}
var initialScope = scope{initialData}
var initialScope = newScope(obj)
initialScope.enter()

state := &state{
Expand Down
36 changes: 25 additions & 11 deletions soyhtml/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,40 @@ package soyhtml

import "github.com/robfig/soy/data"

type scope []data.Map // a stack of variable scopes
// scope handles variable assignment and lookup within a template.
// it is a stack of data maps, each of which corresponds to variable scope.
// assignments made deeper in the stack take precedence over earlier ones.
type scope []scopeframe

// scopeframe is a single piece of the overall variable assignment.
type scopeframe struct {
vars data.Map // map of variable name to value
entered bool // true if this was the initial frame for a template
}

func newScope(m data.Map) scope {
return scope{{m, false}}
}

// push creates a new scope
func (s *scope) push() {
*s = append(*s, make(data.Map))
*s = append(*s, scopeframe{make(data.Map), false})
}

// pop discards the last scope pushed.
func (s *scope) pop() {
*s = (*s)[:len(*s)-1]
}

func (s *scope) augment(m map[string]interface{}) {
*s = append(*s, data.New(m).(data.Map))
}

// set adds a new binding to the deepest scope
func (s scope) set(k string, v data.Value) {
s[len(s)-1][k] = v
s[len(s)-1].vars[k] = v
}

// lookup checks the variable scopes, deepest out, for the given key
func (s scope) lookup(k string) data.Value {
for i := range s {
var elem = s[len(s)-i-1]
var elem = s[len(s)-i-1].vars
if val, ok := elem[k]; ok {
return val
}
Expand All @@ -36,13 +45,18 @@ func (s scope) lookup(k string) data.Value {

// alldata returns a new scope for use when passing data="all" to a template.
func (s scope) alldata() scope {
i := s.lookup("__all").(data.Int)
return s[:i+1 : i+1]
for i := range s {
var ri = len(s) - i - 1
if s[ri].entered {
return s[:ri+1 : ri+1]
}
}
panic("impossible")
}

// enter records that this is the frame where we enter a template.
// only the frames up to here will be passed in the next data="all"
func (s *scope) enter() {
s.set("__all", data.Int(len(*s)-1))
(*s)[len(*s)-1].entered = true
s.push()
}

0 comments on commit 991318f

Please sign in to comment.