f8d9fa9e80
This upgrades all of libgo other than the runtime package to the Go 1.4 release. In Go 1.4 much of the runtime was rewritten into Go. Merging that code will take more time and will not change the API, so I'm putting it off for now. There are a few runtime changes anyhow, to accomodate other packages that rely on minor modifications to the runtime support. The compiler changes slightly to add a one-bit flag to each type descriptor kind that is stored directly in an interface, which for gccgo is currently only pointer types. Another one-bit flag (gcprog) is reserved because it is used by the gc compiler, but gccgo does not currently use it. There is another error check in the compiler since I ran across it during testing. gotools/: * Makefile.am (go_cmd_go_files): Sort entries. Add generate.go. * Makefile.in: Rebuild. From-SVN: r219627
402 lines
11 KiB
Go
402 lines
11 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package template
|
|
|
|
import (
|
|
"bytes"
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestNextJsCtx(t *testing.T) {
|
|
tests := []struct {
|
|
jsCtx jsCtx
|
|
s string
|
|
}{
|
|
// Statement terminators precede regexps.
|
|
{jsCtxRegexp, ";"},
|
|
// This is not airtight.
|
|
// ({ valueOf: function () { return 1 } } / 2)
|
|
// is valid JavaScript but in practice, devs do not do this.
|
|
// A block followed by a statement starting with a RegExp is
|
|
// much more common:
|
|
// while (x) {...} /foo/.test(x) || panic()
|
|
{jsCtxRegexp, "}"},
|
|
// But member, call, grouping, and array expression terminators
|
|
// precede div ops.
|
|
{jsCtxDivOp, ")"},
|
|
{jsCtxDivOp, "]"},
|
|
// At the start of a primary expression, array, or expression
|
|
// statement, expect a regexp.
|
|
{jsCtxRegexp, "("},
|
|
{jsCtxRegexp, "["},
|
|
{jsCtxRegexp, "{"},
|
|
// Assignment operators precede regexps as do all exclusively
|
|
// prefix and binary operators.
|
|
{jsCtxRegexp, "="},
|
|
{jsCtxRegexp, "+="},
|
|
{jsCtxRegexp, "*="},
|
|
{jsCtxRegexp, "*"},
|
|
{jsCtxRegexp, "!"},
|
|
// Whether the + or - is infix or prefix, it cannot precede a
|
|
// div op.
|
|
{jsCtxRegexp, "+"},
|
|
{jsCtxRegexp, "-"},
|
|
// An incr/decr op precedes a div operator.
|
|
// This is not airtight. In (g = ++/h/i) a regexp follows a
|
|
// pre-increment operator, but in practice devs do not try to
|
|
// increment or decrement regular expressions.
|
|
// (g++/h/i) where ++ is a postfix operator on g is much more
|
|
// common.
|
|
{jsCtxDivOp, "--"},
|
|
{jsCtxDivOp, "++"},
|
|
{jsCtxDivOp, "x--"},
|
|
// When we have many dashes or pluses, then they are grouped
|
|
// left to right.
|
|
{jsCtxRegexp, "x---"}, // A postfix -- then a -.
|
|
// return followed by a slash returns the regexp literal or the
|
|
// slash starts a regexp literal in an expression statement that
|
|
// is dead code.
|
|
{jsCtxRegexp, "return"},
|
|
{jsCtxRegexp, "return "},
|
|
{jsCtxRegexp, "return\t"},
|
|
{jsCtxRegexp, "return\n"},
|
|
{jsCtxRegexp, "return\u2028"},
|
|
// Identifiers can be divided and cannot validly be preceded by
|
|
// a regular expressions. Semicolon insertion cannot happen
|
|
// between an identifier and a regular expression on a new line
|
|
// because the one token lookahead for semicolon insertion has
|
|
// to conclude that it could be a div binary op and treat it as
|
|
// such.
|
|
{jsCtxDivOp, "x"},
|
|
{jsCtxDivOp, "x "},
|
|
{jsCtxDivOp, "x\t"},
|
|
{jsCtxDivOp, "x\n"},
|
|
{jsCtxDivOp, "x\u2028"},
|
|
{jsCtxDivOp, "preturn"},
|
|
// Numbers precede div ops.
|
|
{jsCtxDivOp, "0"},
|
|
// Dots that are part of a number are div preceders.
|
|
{jsCtxDivOp, "0."},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx {
|
|
t.Errorf("want %s got %q", test.jsCtx, test.s)
|
|
}
|
|
if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx {
|
|
t.Errorf("want %s got %q", test.jsCtx, test.s)
|
|
}
|
|
}
|
|
|
|
if nextJSCtx([]byte(" "), jsCtxRegexp) != jsCtxRegexp {
|
|
t.Error("Blank tokens")
|
|
}
|
|
|
|
if nextJSCtx([]byte(" "), jsCtxDivOp) != jsCtxDivOp {
|
|
t.Error("Blank tokens")
|
|
}
|
|
}
|
|
|
|
func TestJSValEscaper(t *testing.T) {
|
|
tests := []struct {
|
|
x interface{}
|
|
js string
|
|
}{
|
|
{int(42), " 42 "},
|
|
{uint(42), " 42 "},
|
|
{int16(42), " 42 "},
|
|
{uint16(42), " 42 "},
|
|
{int32(-42), " -42 "},
|
|
{uint32(42), " 42 "},
|
|
{int16(-42), " -42 "},
|
|
{uint16(42), " 42 "},
|
|
{int64(-42), " -42 "},
|
|
{uint64(42), " 42 "},
|
|
{uint64(1) << 53, " 9007199254740992 "},
|
|
// ulp(1 << 53) > 1 so this loses precision in JS
|
|
// but it is still a representable integer literal.
|
|
{uint64(1)<<53 + 1, " 9007199254740993 "},
|
|
{float32(1.0), " 1 "},
|
|
{float32(-1.0), " -1 "},
|
|
{float32(0.5), " 0.5 "},
|
|
{float32(-0.5), " -0.5 "},
|
|
{float32(1.0) / float32(256), " 0.00390625 "},
|
|
{float32(0), " 0 "},
|
|
{math.Copysign(0, -1), " -0 "},
|
|
{float64(1.0), " 1 "},
|
|
{float64(-1.0), " -1 "},
|
|
{float64(0.5), " 0.5 "},
|
|
{float64(-0.5), " -0.5 "},
|
|
{float64(0), " 0 "},
|
|
{math.Copysign(0, -1), " -0 "},
|
|
{"", `""`},
|
|
{"foo", `"foo"`},
|
|
// Newlines.
|
|
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
|
|
// "\v" == "v" on IE 6 so use "\x0b" instead.
|
|
{"\t\x0b", `"\t\u000b"`},
|
|
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
|
|
{[]interface{}{}, "[]"},
|
|
{[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
|
|
{[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
|
|
{"<!--", `"\u003c!--"`},
|
|
{"-->", `"--\u003e"`},
|
|
{"<![CDATA[", `"\u003c![CDATA["`},
|
|
{"]]>", `"]]\u003e"`},
|
|
{"</script", `"\u003c/script"`},
|
|
{"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
|
|
}
|
|
|
|
for _, test := range tests {
|
|
if js := jsValEscaper(test.x); js != test.js {
|
|
t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
|
|
}
|
|
// Make sure that escaping corner cases are not broken
|
|
// by nesting.
|
|
a := []interface{}{test.x}
|
|
want := "[" + strings.TrimSpace(test.js) + "]"
|
|
if js := jsValEscaper(a); js != want {
|
|
t.Errorf("%+v: want\n\t%q\ngot\n\t%q", a, want, js)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestJSStrEscaper(t *testing.T) {
|
|
tests := []struct {
|
|
x interface{}
|
|
esc string
|
|
}{
|
|
{"", ``},
|
|
{"foo", `foo`},
|
|
{"\u0000", `\0`},
|
|
{"\t", `\t`},
|
|
{"\n", `\n`},
|
|
{"\r", `\r`},
|
|
{"\u2028", `\u2028`},
|
|
{"\u2029", `\u2029`},
|
|
{"\\", `\\`},
|
|
{"\\n", `\\n`},
|
|
{"foo\r\nbar", `foo\r\nbar`},
|
|
// Preserve attribute boundaries.
|
|
{`"`, `\x22`},
|
|
{`'`, `\x27`},
|
|
// Allow embedding in HTML without further escaping.
|
|
{`&`, `\x26amp;`},
|
|
// Prevent breaking out of text node and element boundaries.
|
|
{"</script>", `\x3c\/script\x3e`},
|
|
{"<![CDATA[", `\x3c![CDATA[`},
|
|
{"]]>", `]]\x3e`},
|
|
// http://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
|
|
// "The text in style, script, title, and textarea elements
|
|
// must not have an escaping text span start that is not
|
|
// followed by an escaping text span end."
|
|
// Furthermore, spoofing an escaping text span end could lead
|
|
// to different interpretation of a </script> sequence otherwise
|
|
// masked by the escaping text span, and spoofing a start could
|
|
// allow regular text content to be interpreted as script
|
|
// allowing script execution via a combination of a JS string
|
|
// injection followed by an HTML text injection.
|
|
{"<!--", `\x3c!--`},
|
|
{"-->", `--\x3e`},
|
|
// From http://code.google.com/p/doctype/wiki/ArticleUtf7
|
|
{"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
|
|
`\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
|
|
},
|
|
// Invalid UTF-8 sequence
|
|
{"foo\xA0bar", "foo\xA0bar"},
|
|
// Invalid unicode scalar value.
|
|
{"foo\xed\xa0\x80bar", "foo\xed\xa0\x80bar"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
esc := jsStrEscaper(test.x)
|
|
if esc != test.esc {
|
|
t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestJSRegexpEscaper(t *testing.T) {
|
|
tests := []struct {
|
|
x interface{}
|
|
esc string
|
|
}{
|
|
{"", `(?:)`},
|
|
{"foo", `foo`},
|
|
{"\u0000", `\0`},
|
|
{"\t", `\t`},
|
|
{"\n", `\n`},
|
|
{"\r", `\r`},
|
|
{"\u2028", `\u2028`},
|
|
{"\u2029", `\u2029`},
|
|
{"\\", `\\`},
|
|
{"\\n", `\\n`},
|
|
{"foo\r\nbar", `foo\r\nbar`},
|
|
// Preserve attribute boundaries.
|
|
{`"`, `\x22`},
|
|
{`'`, `\x27`},
|
|
// Allow embedding in HTML without further escaping.
|
|
{`&`, `\x26amp;`},
|
|
// Prevent breaking out of text node and element boundaries.
|
|
{"</script>", `\x3c\/script\x3e`},
|
|
{"<![CDATA[", `\x3c!\[CDATA\[`},
|
|
{"]]>", `\]\]\x3e`},
|
|
// Escaping text spans.
|
|
{"<!--", `\x3c!\-\-`},
|
|
{"-->", `\-\-\x3e`},
|
|
{"*", `\*`},
|
|
{"+", `\x2b`},
|
|
{"?", `\?`},
|
|
{"[](){}", `\[\]\(\)\{\}`},
|
|
{"$foo|x.y", `\$foo\|x\.y`},
|
|
{"x^y", `x\^y`},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
esc := jsRegexpEscaper(test.x)
|
|
if esc != test.esc {
|
|
t.Errorf("%q: want %q got %q", test.x, test.esc, esc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
|
|
input := ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f" +
|
|
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
|
` !"#$%&'()*+,-./` +
|
|
`0123456789:;<=>?` +
|
|
`@ABCDEFGHIJKLMNO` +
|
|
`PQRSTUVWXYZ[\]^_` +
|
|
"`abcdefghijklmno" +
|
|
"pqrstuvwxyz{|}~\x7f" +
|
|
"\u00A0\u0100\u2028\u2029\ufeff\U0001D11E")
|
|
|
|
tests := []struct {
|
|
name string
|
|
escaper func(...interface{}) string
|
|
escaped string
|
|
}{
|
|
{
|
|
"jsStrEscaper",
|
|
jsStrEscaper,
|
|
"\\0\x01\x02\x03\x04\x05\x06\x07" +
|
|
"\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
|
|
"\x10\x11\x12\x13\x14\x15\x16\x17" +
|
|
"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
|
` !\x22#$%\x26\x27()*\x2b,-.\/` +
|
|
`0123456789:;\x3c=\x3e?` +
|
|
`@ABCDEFGHIJKLMNO` +
|
|
`PQRSTUVWXYZ[\\]^_` +
|
|
"`abcdefghijklmno" +
|
|
"pqrstuvwxyz{|}~\x7f" +
|
|
"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
|
|
},
|
|
{
|
|
"jsRegexpEscaper",
|
|
jsRegexpEscaper,
|
|
"\\0\x01\x02\x03\x04\x05\x06\x07" +
|
|
"\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
|
|
"\x10\x11\x12\x13\x14\x15\x16\x17" +
|
|
"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
|
` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
|
|
`0123456789:;\x3c=\x3e\?` +
|
|
`@ABCDEFGHIJKLMNO` +
|
|
`PQRSTUVWXYZ\[\\\]\^_` +
|
|
"`abcdefghijklmno" +
|
|
`pqrstuvwxyz\{\|\}~` + "\u007f" +
|
|
"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
if s := test.escaper(input); s != test.escaped {
|
|
t.Errorf("%s once: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
|
|
continue
|
|
}
|
|
|
|
// Escape it rune by rune to make sure that any
|
|
// fast-path checking does not break escaping.
|
|
var buf bytes.Buffer
|
|
for _, c := range input {
|
|
buf.WriteString(test.escaper(string(c)))
|
|
}
|
|
|
|
if s := buf.String(); s != test.escaped {
|
|
t.Errorf("%s rune-wise: want\n\t%q\ngot\n\t%q", test.name, test.escaped, s)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSValEscaperWithNum(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsValEscaper(3.141592654)
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSValEscaperWithStr(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsValEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSValEscaperWithStrNoSpecials(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsValEscaper("The quick, brown fox jumps over the lazy dog")
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSValEscaperWithObj(b *testing.B) {
|
|
o := struct {
|
|
S string
|
|
N int
|
|
}{
|
|
"The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>\u2028",
|
|
42,
|
|
}
|
|
for i := 0; i < b.N; i++ {
|
|
jsValEscaper(o)
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSValEscaperWithObjNoSpecials(b *testing.B) {
|
|
o := struct {
|
|
S string
|
|
N int
|
|
}{
|
|
"The quick, brown fox jumps over the lazy dog",
|
|
42,
|
|
}
|
|
for i := 0; i < b.N; i++ {
|
|
jsValEscaper(o)
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSStrEscaperNoSpecials(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsStrEscaper("The quick, brown fox jumps over the lazy dog.")
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSStrEscaper(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsStrEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSRegexpEscaperNoSpecials(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsRegexpEscaper("The quick, brown fox jumps over the lazy dog")
|
|
}
|
|
}
|
|
|
|
func BenchmarkJSRegexpEscaper(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
jsRegexpEscaper("The <i>quick</i>,\r\n<span style='color:brown'>brown</span> fox jumps\u2028over the <canine class=\"lazy\">dog</canine>")
|
|
}
|
|
}
|