Skip to content

Commit

Permalink
JS: fix escaping of single/double/backtick quotes in strings when enc…
Browse files Browse the repository at this point in the history
…oded as unicode escape literal, fixes #654
  • Loading branch information
tdewolff committed Jan 6, 2024
1 parent e00409b commit 6b45973
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 13 deletions.
2 changes: 1 addition & 1 deletion js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func TestJS(t *testing.T) {
{`"string\∀string"`, `"string∀string"`},
{`"string\0\uFFFFstring"`, "\"string\\0\uffffstring\""},
{`"string\x00\x55\x0A\x0D\x22\x27string"`, "`string\\x00U\n\r\"'string`"},
{`"string\000\12\015\042\47\411string"`, "\"string\\0\\n\\r\\\"'!1string\""},
{`"string\000\12\015\042\47\411string"`, "`string\\0\n\r\"'!1string`"},
{`"\x005"`, `"\x005"`},
{"'string\\n\\rstring'", "`string\n\rstring`"},
{"'string\\\r\nstring\\\nstring\\\rstring\\\u2028string\\\u2029string'", `"stringstringstringstringstringstring"`},
Expand Down
55 changes: 43 additions & 12 deletions js/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,6 @@ func minifyString(b []byte, allowTemplate bool) []byte {
backtickQuotes := 0
newlines := 0
dollarSigns := 0
hasOctals := false
for i := 1; i < len(b)-1; i++ {
if b[i] == '\'' {
singleQuotes++
Expand All @@ -953,36 +952,64 @@ func minifyString(b []byte, allowTemplate bool) []byte {
} else if b[i] == '\\' && i+1 < len(b) {
if b[i+1] == 'n' || b[i+1] == 'r' {
newlines++
} else if '1' <= b[i+1] && b[i+1] <= '9' || b[i+1] == '0' && i+2 < len(b) && '0' <= b[i+2] && b[i+2] <= '9' {
if i+2 < len(b) && b[i+1] == '1' && (b[i+2] == '2' || b[i+2] == '5') {
} else if '1' <= b[i+1] && b[i+1] <= '9' && i+2 < len(b) {
if b[i+1] == '1' && (b[i+2] == '2' || b[i+2] == '5') {
newlines++
} else {
hasOctals = true
} else if b[i+1] == '4' && b[i+2] == '2' {
doubleQuotes++
} else if b[i+1] == '4' && b[i+2] == '7' {
singleQuotes++
} else if i+3 < len(b) && b[i+1] == '1' && b[i+2] == '4' && b[i+3] == '0' {
backtickQuotes++
}
} else if b[i+1] == 'x' && i+3 < len(b) {
if b[i+2] == '0' && (b[i+3]|0x20 == 'a' || b[i+3]|0x20 == 'd') {
newlines++
} else if b[i+2] == '2' && b[i+3] == '2' {
doubleQuotes++
} else if b[i+2] == '2' && b[i+3] == '7' {
singleQuotes++
} else if b[i+2] == '6' && b[i+3] == '0' {
backtickQuotes++
}
} else if b[i+1] == 'u' && i+5 < len(b) && b[i+2] == '0' && b[i+3] == '0' {
if b[i+4] == '0' && (b[i+5]|0x20 == 'a' || b[i+5]|0x20 == 'd') {
newlines++
} else if b[i+4] == '2' && b[i+5] == '2' {
doubleQuotes++
} else if b[i+4] == '2' && b[i+5] == '7' {
singleQuotes++
} else if b[i+4] == '6' && b[i+5] == '0' {
backtickQuotes++
}
} else if b[i+1] == 'x' && i+3 < len(b) && b[i+2] == '0' && (b[i+3]|0x20 == 'a' || b[i+3]|0x20 == 'd') {
newlines++
} else if b[i+1] == 'u' && i+5 < len(b) && b[i+2] == '0' && b[i+3] == '0' && b[i+4] == '0' && (b[i+5]|0x20 == 'a' || b[i+5]|0x20 == 'd') {
newlines++
} else if b[i+1] == 'u' && i+4 < len(b) && b[i+2] == '{' {
j := i + 3
for j < len(b) && b[j] == '0' {
j++
}
if j+1 < len(b) && (b[j]|0x20 == 'a' || b[j]|0x20 == 'd') && b[j+1] == '}' {
newlines++
} else if j+2 < len(b) && b[j+2] == '}' {
if b[j] == '2' && b[j+1] == '2' {
doubleQuotes++
} else if b[j] == '2' && b[j+1] == '7' {
singleQuotes++
} else if b[j] == '6' && b[j+1] == '0' {
backtickQuotes++
}
}
}
}
}
quote := byte('"') // default to " for better GZIP compression
quotes := singleQuotes
quotes := doubleQuotes
if doubleQuotes < singleQuotes {
quote = byte('"')
quotes = doubleQuotes
} else if singleQuotes < doubleQuotes {
quote = byte('\'')
quotes = singleQuotes
}
if allowTemplate && !hasOctals && backtickQuotes+dollarSigns < quotes+newlines {
if allowTemplate && backtickQuotes+dollarSigns < quotes+newlines {
quote = byte('`')
}
b[0] = quote
Expand Down Expand Up @@ -1084,6 +1111,10 @@ func replaceEscapes(b []byte, quote byte, prefix, suffix int) []byte {
if m == -1 {
i++
continue
} else if num < 256 && quote == byte(num) {
b[i] = '\\'
i++
n--
}
utf8.EncodeRune(b[i:], rune(num))
i += m
Expand Down
36 changes: 36 additions & 0 deletions js/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,42 @@ func TestString(t *testing.T) {
{`"\x0A\x0D${${"`, `"\n\r${${"`},
{`"\u000A\u000D${${"`, `"\n\r${${"`},
{`"\u{A}\u{D}${${"`, `"\n\r${${"`},
{`"\42"`, `'"'`},
{`"\x22"`, `'"'`},
{`"\u0022"`, `'"'`},
{`"\u{22}"`, `'"'`},
{`"\42''"`, "`\"''`"},
{`"\x22''"`, "`\"''`"},
{`"\u0022''"`, "`\"''`"},
{`"\u{022}''"`, "`\"''`"},
{"\"\\42''``\"", "\"\\\"''``\""},
{"\"\\x22''``\"", "\"\\\"''``\""},
{"\"\\u0022''``\"", "\"\\\"''``\""},
{"\"\\u{0022}''``\"", "\"\\\"''``\""},
{`'\47'`, `"'"`},
{`'\x27'`, `"'"`},
{`'\u0027'`, `"'"`},
{`'\u{27}'`, `"'"`},
{`'\47""'`, "`'\"\"`"},
{`'\x27""'`, "`'\"\"`"},
{`'\u0027""'`, "`'\"\"`"},
{`'\u{027}""'`, "`'\"\"`"},
{"'\\47\"\"``'", "'\\'\"\"``'"},
{"'\\x27\"\"``'", "'\\'\"\"``'"},
{"'\\u0027\"\"``'", "'\\'\"\"``'"},
{"'\\u{0027}\"\"``'", "'\\'\"\"``'"},
{`'\140'`, "\"`\""},
{`'\x60'`, "\"`\""},
{`'\u0060'`, "\"`\""},
{`'\u{60}'`, "\"`\""},
{`'\140""'`, "'`\"\"'"},
{`'\x60""'`, "'`\"\"'"},
{`'\u0060""'`, "'`\"\"'"},
{`'\u{060}""'`, "'`\"\"'"},
{`'\140""\'\''`, "`\\`\"\"''`"},
{`'\x60""\'\''`, "`\\`\"\"''`"},
{`'\u0060""\'\''`, "`\\`\"\"''`"},
{`'\u{0060}""\'\''`, "`\\`\"\"''`"},
}

for _, tt := range tests {
Expand Down

0 comments on commit 6b45973

Please sign in to comment.