| package dbus |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "io" |
| "reflect" |
| "strconv" |
| "strings" |
| "unicode/utf8" |
| ) |
| |
| type varParser struct { |
| tokens []varToken |
| i int |
| } |
| |
| func (p *varParser) backup() { |
| p.i-- |
| } |
| |
| func (p *varParser) next() varToken { |
| if p.i < len(p.tokens) { |
| t := p.tokens[p.i] |
| p.i++ |
| return t |
| } |
| return varToken{typ: tokEOF} |
| } |
| |
| type varNode interface { |
| Infer() (Signature, error) |
| String() string |
| Sigs() sigSet |
| Value(Signature) (interface{}, error) |
| } |
| |
| func varMakeNode(p *varParser) (varNode, error) { |
| var sig Signature |
| |
| for { |
| t := p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| case tokNumber: |
| return varMakeNumNode(t, sig) |
| case tokString: |
| return varMakeStringNode(t, sig) |
| case tokBool: |
| if sig.str != "" && sig.str != "b" { |
| return nil, varTypeError{t.val, sig} |
| } |
| b, err := strconv.ParseBool(t.val) |
| if err != nil { |
| return nil, err |
| } |
| return boolNode(b), nil |
| case tokArrayStart: |
| return varMakeArrayNode(p, sig) |
| case tokVariantStart: |
| return varMakeVariantNode(p, sig) |
| case tokDictStart: |
| return varMakeDictNode(p, sig) |
| case tokType: |
| if sig.str != "" { |
| return nil, errors.New("unexpected type annotation") |
| } |
| if t.val[0] == '@' { |
| sig.str = t.val[1:] |
| } else { |
| sig.str = varTypeMap[t.val] |
| } |
| case tokByteString: |
| if sig.str != "" && sig.str != "ay" { |
| return nil, varTypeError{t.val, sig} |
| } |
| b, err := varParseByteString(t.val) |
| if err != nil { |
| return nil, err |
| } |
| return byteStringNode(b), nil |
| default: |
| return nil, fmt.Errorf("unexpected %q", t.val) |
| } |
| } |
| } |
| |
| type varTypeError struct { |
| val string |
| sig Signature |
| } |
| |
| func (e varTypeError) Error() string { |
| return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str) |
| } |
| |
| type sigSet map[Signature]bool |
| |
| func (s sigSet) Empty() bool { |
| return len(s) == 0 |
| } |
| |
| func (s sigSet) Intersect(s2 sigSet) sigSet { |
| r := make(sigSet) |
| for k := range s { |
| if s2[k] { |
| r[k] = true |
| } |
| } |
| return r |
| } |
| |
| func (s sigSet) Single() (Signature, bool) { |
| if len(s) == 1 { |
| for k := range s { |
| return k, true |
| } |
| } |
| return Signature{}, false |
| } |
| |
| func (s sigSet) ToArray() sigSet { |
| r := make(sigSet, len(s)) |
| for k := range s { |
| r[Signature{"a" + k.str}] = true |
| } |
| return r |
| } |
| |
| type numNode struct { |
| sig Signature |
| str string |
| val interface{} |
| } |
| |
| var numSigSet = sigSet{ |
| Signature{"y"}: true, |
| Signature{"n"}: true, |
| Signature{"q"}: true, |
| Signature{"i"}: true, |
| Signature{"u"}: true, |
| Signature{"x"}: true, |
| Signature{"t"}: true, |
| Signature{"d"}: true, |
| } |
| |
| func (n numNode) Infer() (Signature, error) { |
| if strings.ContainsAny(n.str, ".e") { |
| return Signature{"d"}, nil |
| } |
| return Signature{"i"}, nil |
| } |
| |
| func (n numNode) String() string { |
| return n.str |
| } |
| |
| func (n numNode) Sigs() sigSet { |
| if n.sig.str != "" { |
| return sigSet{n.sig: true} |
| } |
| if strings.ContainsAny(n.str, ".e") { |
| return sigSet{Signature{"d"}: true} |
| } |
| return numSigSet |
| } |
| |
| func (n numNode) Value(sig Signature) (interface{}, error) { |
| if n.sig.str != "" && n.sig != sig { |
| return nil, varTypeError{n.str, sig} |
| } |
| if n.val != nil { |
| return n.val, nil |
| } |
| return varNumAs(n.str, sig) |
| } |
| |
| func varMakeNumNode(tok varToken, sig Signature) (varNode, error) { |
| if sig.str == "" { |
| return numNode{str: tok.val}, nil |
| } |
| num, err := varNumAs(tok.val, sig) |
| if err != nil { |
| return nil, err |
| } |
| return numNode{sig: sig, val: num}, nil |
| } |
| |
| func varNumAs(s string, sig Signature) (interface{}, error) { |
| isUnsigned := false |
| size := 32 |
| switch sig.str { |
| case "n": |
| size = 16 |
| case "i": |
| case "x": |
| size = 64 |
| case "y": |
| size = 8 |
| isUnsigned = true |
| case "q": |
| size = 16 |
| isUnsigned = true |
| case "u": |
| isUnsigned = true |
| case "t": |
| size = 64 |
| isUnsigned = true |
| case "d": |
| d, err := strconv.ParseFloat(s, 64) |
| if err != nil { |
| return nil, err |
| } |
| return d, nil |
| default: |
| return nil, varTypeError{s, sig} |
| } |
| base := 10 |
| if strings.HasPrefix(s, "0x") { |
| base = 16 |
| s = s[2:] |
| } |
| if strings.HasPrefix(s, "0") && len(s) != 1 { |
| base = 8 |
| s = s[1:] |
| } |
| if isUnsigned { |
| i, err := strconv.ParseUint(s, base, size) |
| if err != nil { |
| return nil, err |
| } |
| var v interface{} = i |
| switch sig.str { |
| case "y": |
| v = byte(i) |
| case "q": |
| v = uint16(i) |
| case "u": |
| v = uint32(i) |
| } |
| return v, nil |
| } |
| i, err := strconv.ParseInt(s, base, size) |
| if err != nil { |
| return nil, err |
| } |
| var v interface{} = i |
| switch sig.str { |
| case "n": |
| v = int16(i) |
| case "i": |
| v = int32(i) |
| } |
| return v, nil |
| } |
| |
| type stringNode struct { |
| sig Signature |
| str string // parsed |
| val interface{} // has correct type |
| } |
| |
| var stringSigSet = sigSet{ |
| Signature{"s"}: true, |
| Signature{"g"}: true, |
| Signature{"o"}: true, |
| } |
| |
| func (n stringNode) Infer() (Signature, error) { |
| return Signature{"s"}, nil |
| } |
| |
| func (n stringNode) String() string { |
| return n.str |
| } |
| |
| func (n stringNode) Sigs() sigSet { |
| if n.sig.str != "" { |
| return sigSet{n.sig: true} |
| } |
| return stringSigSet |
| } |
| |
| func (n stringNode) Value(sig Signature) (interface{}, error) { |
| if n.sig.str != "" && n.sig != sig { |
| return nil, varTypeError{n.str, sig} |
| } |
| if n.val != nil { |
| return n.val, nil |
| } |
| switch { |
| case sig.str == "g": |
| return Signature{n.str}, nil |
| case sig.str == "o": |
| return ObjectPath(n.str), nil |
| case sig.str == "s": |
| return n.str, nil |
| default: |
| return nil, varTypeError{n.str, sig} |
| } |
| } |
| |
| func varMakeStringNode(tok varToken, sig Signature) (varNode, error) { |
| if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" { |
| return nil, fmt.Errorf("invalid type %q for string", sig.str) |
| } |
| s, err := varParseString(tok.val) |
| if err != nil { |
| return nil, err |
| } |
| n := stringNode{str: s} |
| if sig.str == "" { |
| return stringNode{str: s}, nil |
| } |
| n.sig = sig |
| switch sig.str { |
| case "o": |
| n.val = ObjectPath(s) |
| case "g": |
| n.val = Signature{s} |
| case "s": |
| n.val = s |
| } |
| return n, nil |
| } |
| |
| func varParseString(s string) (string, error) { |
| // quotes are guaranteed to be there |
| s = s[1 : len(s)-1] |
| buf := new(bytes.Buffer) |
| for len(s) != 0 { |
| r, size := utf8.DecodeRuneInString(s) |
| if r == utf8.RuneError && size == 1 { |
| return "", errors.New("invalid UTF-8") |
| } |
| s = s[size:] |
| if r != '\\' { |
| buf.WriteRune(r) |
| continue |
| } |
| r, size = utf8.DecodeRuneInString(s) |
| if r == utf8.RuneError && size == 1 { |
| return "", errors.New("invalid UTF-8") |
| } |
| s = s[size:] |
| switch r { |
| case 'a': |
| buf.WriteRune(0x7) |
| case 'b': |
| buf.WriteRune(0x8) |
| case 'f': |
| buf.WriteRune(0xc) |
| case 'n': |
| buf.WriteRune('\n') |
| case 'r': |
| buf.WriteRune('\r') |
| case 't': |
| buf.WriteRune('\t') |
| case '\n': |
| case 'u': |
| if len(s) < 4 { |
| return "", errors.New("short unicode escape") |
| } |
| r, err := strconv.ParseUint(s[:4], 16, 32) |
| if err != nil { |
| return "", err |
| } |
| buf.WriteRune(rune(r)) |
| s = s[4:] |
| case 'U': |
| if len(s) < 8 { |
| return "", errors.New("short unicode escape") |
| } |
| r, err := strconv.ParseUint(s[:8], 16, 32) |
| if err != nil { |
| return "", err |
| } |
| buf.WriteRune(rune(r)) |
| s = s[8:] |
| default: |
| buf.WriteRune(r) |
| } |
| } |
| return buf.String(), nil |
| } |
| |
| var boolSigSet = sigSet{Signature{"b"}: true} |
| |
| type boolNode bool |
| |
| func (boolNode) Infer() (Signature, error) { |
| return Signature{"b"}, nil |
| } |
| |
| func (b boolNode) String() string { |
| if b { |
| return "true" |
| } |
| return "false" |
| } |
| |
| func (boolNode) Sigs() sigSet { |
| return boolSigSet |
| } |
| |
| func (b boolNode) Value(sig Signature) (interface{}, error) { |
| if sig.str != "b" { |
| return nil, varTypeError{b.String(), sig} |
| } |
| return bool(b), nil |
| } |
| |
| type arrayNode struct { |
| set sigSet |
| children []varNode |
| val interface{} |
| } |
| |
| func (n arrayNode) Infer() (Signature, error) { |
| for _, v := range n.children { |
| csig, err := varInfer(v) |
| if err != nil { |
| continue |
| } |
| return Signature{"a" + csig.str}, nil |
| } |
| return Signature{}, fmt.Errorf("can't infer type for %q", n.String()) |
| } |
| |
| func (n arrayNode) String() string { |
| s := "[" |
| for i, v := range n.children { |
| s += v.String() |
| if i != len(n.children)-1 { |
| s += ", " |
| } |
| } |
| return s + "]" |
| } |
| |
| func (n arrayNode) Sigs() sigSet { |
| return n.set |
| } |
| |
| func (n arrayNode) Value(sig Signature) (interface{}, error) { |
| if n.set.Empty() { |
| // no type information whatsoever, so this must be an empty slice |
| return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil |
| } |
| if !n.set[sig] { |
| return nil, varTypeError{n.String(), sig} |
| } |
| s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children)) |
| for i, v := range n.children { |
| rv, err := v.Value(Signature{sig.str[1:]}) |
| if err != nil { |
| return nil, err |
| } |
| s.Index(i).Set(reflect.ValueOf(rv)) |
| } |
| return s.Interface(), nil |
| } |
| |
| func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) { |
| var n arrayNode |
| if sig.str != "" { |
| n.set = sigSet{sig: true} |
| } |
| if t := p.next(); t.typ == tokArrayEnd { |
| return n, nil |
| } else { |
| p.backup() |
| } |
| Loop: |
| for { |
| t := p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| } |
| p.backup() |
| cn, err := varMakeNode(p) |
| if err != nil { |
| return nil, err |
| } |
| if cset := cn.Sigs(); !cset.Empty() { |
| if n.set.Empty() { |
| n.set = cset.ToArray() |
| } else { |
| nset := cset.ToArray().Intersect(n.set) |
| if nset.Empty() { |
| return nil, fmt.Errorf("can't parse %q with given type information", cn.String()) |
| } |
| n.set = nset |
| } |
| } |
| n.children = append(n.children, cn) |
| switch t := p.next(); t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| case tokArrayEnd: |
| break Loop |
| case tokComma: |
| continue |
| default: |
| return nil, fmt.Errorf("unexpected %q", t.val) |
| } |
| } |
| return n, nil |
| } |
| |
| type variantNode struct { |
| n varNode |
| } |
| |
| var variantSet = sigSet{ |
| Signature{"v"}: true, |
| } |
| |
| func (variantNode) Infer() (Signature, error) { |
| return Signature{"v"}, nil |
| } |
| |
| func (n variantNode) String() string { |
| return "<" + n.n.String() + ">" |
| } |
| |
| func (variantNode) Sigs() sigSet { |
| return variantSet |
| } |
| |
| func (n variantNode) Value(sig Signature) (interface{}, error) { |
| if sig.str != "v" { |
| return nil, varTypeError{n.String(), sig} |
| } |
| sig, err := varInfer(n.n) |
| if err != nil { |
| return nil, err |
| } |
| v, err := n.n.Value(sig) |
| if err != nil { |
| return nil, err |
| } |
| return MakeVariant(v), nil |
| } |
| |
| func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) { |
| n, err := varMakeNode(p) |
| if err != nil { |
| return nil, err |
| } |
| if t := p.next(); t.typ != tokVariantEnd { |
| return nil, fmt.Errorf("unexpected %q", t.val) |
| } |
| vn := variantNode{n} |
| if sig.str != "" && sig.str != "v" { |
| return nil, varTypeError{vn.String(), sig} |
| } |
| return variantNode{n}, nil |
| } |
| |
| type dictEntry struct { |
| key, val varNode |
| } |
| |
| type dictNode struct { |
| kset, vset sigSet |
| children []dictEntry |
| val interface{} |
| } |
| |
| func (n dictNode) Infer() (Signature, error) { |
| for _, v := range n.children { |
| ksig, err := varInfer(v.key) |
| if err != nil { |
| continue |
| } |
| vsig, err := varInfer(v.val) |
| if err != nil { |
| continue |
| } |
| return Signature{"a{" + ksig.str + vsig.str + "}"}, nil |
| } |
| return Signature{}, fmt.Errorf("can't infer type for %q", n.String()) |
| } |
| |
| func (n dictNode) String() string { |
| s := "{" |
| for i, v := range n.children { |
| s += v.key.String() + ": " + v.val.String() |
| if i != len(n.children)-1 { |
| s += ", " |
| } |
| } |
| return s + "}" |
| } |
| |
| func (n dictNode) Sigs() sigSet { |
| r := sigSet{} |
| for k := range n.kset { |
| for v := range n.vset { |
| sig := "a{" + k.str + v.str + "}" |
| r[Signature{sig}] = true |
| } |
| } |
| return r |
| } |
| |
| func (n dictNode) Value(sig Signature) (interface{}, error) { |
| set := n.Sigs() |
| if set.Empty() { |
| // no type information -> empty dict |
| return reflect.MakeMap(typeFor(sig.str)).Interface(), nil |
| } |
| if !set[sig] { |
| return nil, varTypeError{n.String(), sig} |
| } |
| m := reflect.MakeMap(typeFor(sig.str)) |
| ksig := Signature{sig.str[2:3]} |
| vsig := Signature{sig.str[3 : len(sig.str)-1]} |
| for _, v := range n.children { |
| kv, err := v.key.Value(ksig) |
| if err != nil { |
| return nil, err |
| } |
| vv, err := v.val.Value(vsig) |
| if err != nil { |
| return nil, err |
| } |
| m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) |
| } |
| return m.Interface(), nil |
| } |
| |
| func varMakeDictNode(p *varParser, sig Signature) (varNode, error) { |
| var n dictNode |
| |
| if sig.str != "" { |
| if len(sig.str) < 5 { |
| return nil, fmt.Errorf("invalid signature %q for dict type", sig) |
| } |
| ksig := Signature{string(sig.str[2])} |
| vsig := Signature{sig.str[3 : len(sig.str)-1]} |
| n.kset = sigSet{ksig: true} |
| n.vset = sigSet{vsig: true} |
| } |
| if t := p.next(); t.typ == tokDictEnd { |
| return n, nil |
| } else { |
| p.backup() |
| } |
| Loop: |
| for { |
| t := p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| } |
| p.backup() |
| kn, err := varMakeNode(p) |
| if err != nil { |
| return nil, err |
| } |
| if kset := kn.Sigs(); !kset.Empty() { |
| if n.kset.Empty() { |
| n.kset = kset |
| } else { |
| n.kset = kset.Intersect(n.kset) |
| if n.kset.Empty() { |
| return nil, fmt.Errorf("can't parse %q with given type information", kn.String()) |
| } |
| } |
| } |
| t = p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| case tokColon: |
| default: |
| return nil, fmt.Errorf("unexpected %q", t.val) |
| } |
| t = p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| } |
| p.backup() |
| vn, err := varMakeNode(p) |
| if err != nil { |
| return nil, err |
| } |
| if vset := vn.Sigs(); !vset.Empty() { |
| if n.vset.Empty() { |
| n.vset = vset |
| } else { |
| n.vset = n.vset.Intersect(vset) |
| if n.vset.Empty() { |
| return nil, fmt.Errorf("can't parse %q with given type information", vn.String()) |
| } |
| } |
| } |
| n.children = append(n.children, dictEntry{kn, vn}) |
| t = p.next() |
| switch t.typ { |
| case tokEOF: |
| return nil, io.ErrUnexpectedEOF |
| case tokError: |
| return nil, errors.New(t.val) |
| case tokDictEnd: |
| break Loop |
| case tokComma: |
| continue |
| default: |
| return nil, fmt.Errorf("unexpected %q", t.val) |
| } |
| } |
| return n, nil |
| } |
| |
| type byteStringNode []byte |
| |
| var byteStringSet = sigSet{ |
| Signature{"ay"}: true, |
| } |
| |
| func (byteStringNode) Infer() (Signature, error) { |
| return Signature{"ay"}, nil |
| } |
| |
| func (b byteStringNode) String() string { |
| return string(b) |
| } |
| |
| func (b byteStringNode) Sigs() sigSet { |
| return byteStringSet |
| } |
| |
| func (b byteStringNode) Value(sig Signature) (interface{}, error) { |
| if sig.str != "ay" { |
| return nil, varTypeError{b.String(), sig} |
| } |
| return []byte(b), nil |
| } |
| |
| func varParseByteString(s string) ([]byte, error) { |
| // quotes and b at start are guaranteed to be there |
| b := make([]byte, 0, 1) |
| s = s[2 : len(s)-1] |
| for len(s) != 0 { |
| c := s[0] |
| s = s[1:] |
| if c != '\\' { |
| b = append(b, c) |
| continue |
| } |
| c = s[0] |
| s = s[1:] |
| switch c { |
| case 'a': |
| b = append(b, 0x7) |
| case 'b': |
| b = append(b, 0x8) |
| case 'f': |
| b = append(b, 0xc) |
| case 'n': |
| b = append(b, '\n') |
| case 'r': |
| b = append(b, '\r') |
| case 't': |
| b = append(b, '\t') |
| case 'x': |
| if len(s) < 2 { |
| return nil, errors.New("short escape") |
| } |
| n, err := strconv.ParseUint(s[:2], 16, 8) |
| if err != nil { |
| return nil, err |
| } |
| b = append(b, byte(n)) |
| s = s[2:] |
| case '0': |
| if len(s) < 3 { |
| return nil, errors.New("short escape") |
| } |
| n, err := strconv.ParseUint(s[:3], 8, 8) |
| if err != nil { |
| return nil, err |
| } |
| b = append(b, byte(n)) |
| s = s[3:] |
| default: |
| b = append(b, c) |
| } |
| } |
| return append(b, 0), nil |
| } |
| |
| func varInfer(n varNode) (Signature, error) { |
| if sig, ok := n.Sigs().Single(); ok { |
| return sig, nil |
| } |
| return n.Infer() |
| } |