aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/reflect
diff options
context:
space:
mode:
authorDamian Gryski <[email protected]>2023-03-24 13:48:00 -0700
committerRon Evans <[email protected]>2023-03-30 21:10:54 +0200
commit5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7 (patch)
treeadf45db8d4184a4fce528f64d5f5bfbd69ac986c /src/reflect
parent195de23d3b793f5e9188d4f0c0543cdb6bbf01dd (diff)
downloadtinygo-5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7.tar.gz
tinygo-5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7.zip
reflect: add sipmlified strconv.Quote() implementation for struct tags
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/all_test.go2
-rw-r--r--src/reflect/strconv.go99
-rw-r--r--src/reflect/type.go6
3 files changed, 101 insertions, 6 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index ce1abef7f..dea02077f 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -188,7 +188,6 @@ var typeTests = []pair{
}{},
"struct { a int8; b int8; c int8; d int8; e int8; f int32 }",
},
- /* // TODO(tinygo): Reflects tags aren't properly quoted
{struct {
x struct {
a int8 `reflect:"hi there"`
@@ -203,6 +202,7 @@ var typeTests = []pair{
}{},
`struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`,
},
+ /* // TODO(tinygo): Functions not supported
{struct {
x struct {
f func(args ...int)
diff --git a/src/reflect/strconv.go b/src/reflect/strconv.go
index b75d51341..2d8131c9d 100644
--- a/src/reflect/strconv.go
+++ b/src/reflect/strconv.go
@@ -30,6 +30,10 @@ func unhex(b byte) (v rune, ok bool) {
return
}
+const (
+ lowerhex = "0123456789abcef"
+)
+
// unquoteChar decodes the first character or byte in the escaped string
// or character literal represented by the string s.
// It returns four values:
@@ -231,6 +235,101 @@ func unquote(s string) (string, error) {
return string(buf), nil
}
+func quote(s string) string {
+ buf := make([]byte, 0, 3*len(s)/2)
+ const quote = '"'
+
+ buf = append(buf, quote)
+ for width := 0; len(s) > 0; s = s[width:] {
+ r := rune(s[0])
+ width = 1
+ if r >= utf8.RuneSelf {
+ r, width = utf8.DecodeRuneInString(s)
+ }
+ if width == 1 && r == utf8.RuneError {
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[s[0]>>4])
+ buf = append(buf, lowerhex[s[0]&0xF])
+ continue
+ }
+ buf = appendEscapedRune(buf, r)
+ }
+ buf = append(buf, quote)
+ return string(buf)
+}
+
+func appendEscapedRune(buf []byte, r rune) []byte {
+
+ const quote = '"'
+
+ var runeTmp [utf8.UTFMax]byte
+ if r == rune(quote) || r == '\\' { // always backslashed
+ buf = append(buf, '\\')
+ buf = append(buf, byte(r))
+ return buf
+ }
+ if isPrint(r) {
+ n := utf8.EncodeRune(runeTmp[:], r)
+ buf = append(buf, runeTmp[:n]...)
+ return buf
+ }
+ switch r {
+ case '\a':
+ buf = append(buf, `\a`...)
+ case '\b':
+ buf = append(buf, `\b`...)
+ case '\f':
+ buf = append(buf, `\f`...)
+ case '\n':
+ buf = append(buf, `\n`...)
+ case '\r':
+ buf = append(buf, `\r`...)
+ case '\t':
+ buf = append(buf, `\t`...)
+ case '\v':
+ buf = append(buf, `\v`...)
+ default:
+ switch {
+ case r < ' ' || r == 0x7f:
+ buf = append(buf, `\x`...)
+ buf = append(buf, lowerhex[byte(r)>>4])
+ buf = append(buf, lowerhex[byte(r)&0xF])
+ case !utf8.ValidRune(r):
+ r = 0xFFFD
+ fallthrough
+ case r < 0x10000:
+ buf = append(buf, `\u`...)
+ for s := 12; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ default:
+ buf = append(buf, `\U`...)
+ for s := 28; s >= 0; s -= 4 {
+ buf = append(buf, lowerhex[r>>uint(s)&0xF])
+ }
+ }
+ }
+ return buf
+}
+
+// This is only used for struct tags. Assume
+func isPrint(r rune) bool {
+ if r <= 0xFF {
+ if 0x20 <= r && r <= 0x7E {
+ // All the ASCII is printable from space through DEL-1.
+ return true
+ }
+ if 0xA1 <= r && r <= 0xFF {
+ // Similarly for ¡ through ÿ...
+ return r != 0xAD // ...except for the bizarre soft hyphen.
+ }
+ return false
+ }
+
+ // TinyGo: Skip all other unicode processing
+ return false
+}
+
// contains reports whether the string contains the byte c.
func contains(s string, c byte) bool {
return indexByteString(s, c) != -1
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 1c1a17bb9..814cf39a4 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -554,11 +554,7 @@ func (t *rawType) String() string {
f := t.rawField(i)
s += " " + f.Name + " " + f.Type.String()
if f.Tag != "" {
- // TODO(dgryski): The tag should be
- // double-quoted and escaped; that requires
- // strconv and reflectlite or our own Quote()
- // implementation
- s += " " + string(f.Tag)
+ s += " " + quote(string(f.Tag))
}
// every field except the last needs a semicolon
if i < numField-1 {