summaryrefslogtreecommitdiffstats
path: root/tpl/internal/go_templates/htmltemplate/transition.go
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/internal/go_templates/htmltemplate/transition.go')
-rw-r--r--tpl/internal/go_templates/htmltemplate/transition.go62
1 files changed, 57 insertions, 5 deletions
diff --git a/tpl/internal/go_templates/htmltemplate/transition.go b/tpl/internal/go_templates/htmltemplate/transition.go
index 432c365d3..d5a05f66d 100644
--- a/tpl/internal/go_templates/htmltemplate/transition.go
+++ b/tpl/internal/go_templates/htmltemplate/transition.go
@@ -27,8 +27,8 @@ var transitionFunc = [...]func(context, []byte) (context, int){
stateJS: tJS,
stateJSDqStr: tJSDelimited,
stateJSSqStr: tJSDelimited,
- stateJSBqStr: tJSDelimited,
stateJSRegexp: tJSDelimited,
+ stateJSTmplLit: tJSTmpl,
stateJSBlockCmt: tBlockCmt,
stateJSLineCmt: tLineCmt,
stateJSHTMLOpenCmt: tLineCmt,
@@ -270,7 +270,7 @@ func tURL(c context, s []byte) (context, int) {
// tJS is the context transition function for the JS state.
func tJS(c context, s []byte) (context, int) {
- i := bytes.IndexAny(s, "\"`'/<-#")
+ i := bytes.IndexAny(s, "\"`'/{}<-#")
if i == -1 {
// Entire input is non string, comment, regexp tokens.
c.jsCtx = nextJSCtx(s, c.jsCtx)
@@ -283,7 +283,7 @@ func tJS(c context, s []byte) (context, int) {
case '\'':
c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
case '`':
- c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp
+ c.state, c.jsCtx = stateJSTmplLit, jsCtxRegexp
case '/':
switch {
case i+1 < len(s) && s[i+1] == '/':
@@ -320,12 +320,66 @@ func tJS(c context, s []byte) (context, int) {
if i+1 < len(s) && s[i+1] == '!' {
c.state, i = stateJSLineCmt, i+1
}
+ case '{':
+ // We only care about tracking brace depth if we are inside of a
+ // template literal.
+ if len(c.jsBraceDepth) == 0 {
+ return c, i + 1
+ }
+ c.jsBraceDepth[len(c.jsBraceDepth)-1]++
+ case '}':
+ if len(c.jsBraceDepth) == 0 {
+ return c, i + 1
+ }
+ // There are no cases where a brace can be escaped in the JS context
+ // that are not syntax errors, it seems. Because of this we can just
+ // count "\}" as "}" and move on, the script is already broken as
+ // fully fledged parsers will just fail anyway.
+ c.jsBraceDepth[len(c.jsBraceDepth)-1]--
+ if c.jsBraceDepth[len(c.jsBraceDepth)-1] >= 0 {
+ return c, i + 1
+ }
+ c.jsBraceDepth = c.jsBraceDepth[:len(c.jsBraceDepth)-1]
+ c.state = stateJSTmplLit
default:
panic("unreachable")
}
return c, i + 1
}
+func tJSTmpl(c context, s []byte) (context, int) {
+ var k int
+ for {
+ i := k + bytes.IndexAny(s[k:], "`\\$")
+ if i < k {
+ break
+ }
+ switch s[i] {
+ case '\\':
+ i++
+ if i == len(s) {
+ return context{
+ state: stateError,
+ err: errorf(ErrPartialEscape, nil, 0, "unfinished escape sequence in JS string: %q", s),
+ }, len(s)
+ }
+ case '$':
+ if len(s) >= i+2 && s[i+1] == '{' {
+ c.jsBraceDepth = append(c.jsBraceDepth, 0)
+ c.state = stateJS
+ return c, i + 2
+ }
+ case '`':
+ // end
+ c.state = stateJS
+ return c, i + 1
+ }
+ k = i + 1
+ }
+
+ return c, len(s)
+}
+
// tJSDelimited is the context transition function for the JS string and regexp
// states.
func tJSDelimited(c context, s []byte) (context, int) {
@@ -333,8 +387,6 @@ func tJSDelimited(c context, s []byte) (context, int) {
switch c.state {
case stateJSSqStr:
specials = `\'`
- case stateJSBqStr:
- specials = "`\\"
case stateJSRegexp:
specials = `\/[]`
}