aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/serializer
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-08-28 15:59:22 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-02 13:06:53 +0200
commita7206b24cac96c08aecf2f3b4cc3c2e555eec708 (patch)
tree80c678141148ce2eafaab5617f168bd840b8c8a6 /pkg/serializer
parentaa51461a34f998908d10f551615ad242bdff8fe9 (diff)
pkg/compiler: check and generate types
Move most of the logic from sysgen to pkg/compiler. Update #217
Diffstat (limited to 'pkg/serializer')
-rw-r--r--pkg/serializer/serializer.go165
-rw-r--r--pkg/serializer/serializer_test.go44
2 files changed, 209 insertions, 0 deletions
diff --git a/pkg/serializer/serializer.go b/pkg/serializer/serializer.go
new file mode 100644
index 000000000..2b1bbb905
--- /dev/null
+++ b/pkg/serializer/serializer.go
@@ -0,0 +1,165 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package serializer
+
+import (
+ "reflect"
+
+ "fmt"
+ "io"
+)
+
+// Write writes Go-syntax representation of v into w.
+// This is similar to fmt.Fprintf(w, "%#v", v), but properly handles pointers,
+// does not write package names before types, omits struct fields with default values,
+// omits type names where possible, etc. On the other hand, it currently does not
+// support all types (e.g. channels and maps).
+func Write(w io.Writer, v interface{}) {
+ ww := writer{w}
+ ww.do(reflect.ValueOf(v), false)
+}
+
+type writer struct {
+ w io.Writer
+}
+
+func (w *writer) do(v reflect.Value, sliceElem bool) {
+ switch v.Kind() {
+ case reflect.Ptr:
+ if v.IsNil() {
+ w.string("nil")
+ return
+ }
+ if !sliceElem {
+ w.byte('&')
+ }
+ if v.Elem().Kind() != reflect.Struct {
+ panic(fmt.Sprintf("only pointers to structs are supported, got %v",
+ v.Type().Name()))
+ }
+ w.do(v.Elem(), sliceElem)
+ case reflect.Interface:
+ if v.IsNil() {
+ w.string("nil")
+ } else {
+ w.do(v.Elem(), false)
+ }
+ case reflect.Slice:
+ if v.IsNil() || v.Len() == 0 {
+ w.string("nil")
+ } else {
+ w.typ(v.Type())
+ if sub := v.Type().Elem().Kind(); sub == reflect.Ptr || sub == reflect.Interface {
+ // Elem per-line.
+ w.string("{\n")
+ for i := 0; i < v.Len(); i++ {
+ w.do(v.Index(i), true)
+ w.string(",\n")
+ }
+ w.byte('}')
+ } else {
+ // All on one line.
+ w.byte('{')
+ for i := 0; i < v.Len(); i++ {
+ if i > 0 {
+ w.byte(',')
+ }
+ w.do(v.Index(i), true)
+ }
+ w.byte('}')
+ }
+ }
+ case reflect.Struct:
+ if !sliceElem {
+ w.string(v.Type().Name())
+ }
+ w.byte('{')
+ needComma := false
+ for i := 0; i < v.NumField(); i++ {
+ f := v.Field(i)
+ if isDefaultValue(f) {
+ continue
+ }
+ if needComma {
+ w.byte(',')
+ }
+ w.string(v.Type().Field(i).Name)
+ w.byte(':')
+ w.do(f, false)
+ needComma = true
+ }
+ w.byte('}')
+ case reflect.Bool:
+ if v.Bool() {
+ w.string("true")
+ } else {
+ w.string("false")
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ fmt.Fprintf(w.w, "%v", v.Int())
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ fmt.Fprintf(w.w, "%v", v.Uint())
+ case reflect.String:
+ fmt.Fprintf(w.w, "%q", v.String())
+ default:
+ panic(fmt.Sprintf("unsupported type: %#v", v.Type().String()))
+ }
+}
+
+func (w *writer) typ(t reflect.Type) {
+ switch t.Kind() {
+ case reflect.Ptr:
+ w.byte('*')
+ w.typ(t.Elem())
+ case reflect.Slice:
+ w.string("[]")
+ w.typ(t.Elem())
+ default:
+ w.string(t.Name())
+ }
+}
+
+func (w *writer) write(v []byte) {
+ w.w.Write(v)
+}
+
+func (w *writer) string(v string) {
+ io.WriteString(w.w, v)
+}
+
+func (w *writer) byte(v byte) {
+ if bw, ok := w.w.(io.ByteWriter); ok {
+ bw.WriteByte(v)
+ } else {
+ w.w.Write([]byte{v})
+ }
+}
+
+func isDefaultValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Ptr:
+ return v.IsNil()
+ case reflect.Interface:
+ return v.IsNil()
+ case reflect.Slice:
+ return v.IsNil() || v.Len() == 0
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ if !isDefaultValue(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.String:
+ return v.String() == ""
+ default:
+ return false
+ }
+}
diff --git a/pkg/serializer/serializer_test.go b/pkg/serializer/serializer_test.go
new file mode 100644
index 000000000..17cc9550b
--- /dev/null
+++ b/pkg/serializer/serializer_test.go
@@ -0,0 +1,44 @@
+// Copyright 2017 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package serializer
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestSerializer(t *testing.T) {
+ x := &X{
+ Y: Y{1},
+ P: &Y{2},
+ A: []Y{Y{3}, Y{4}},
+ F: true,
+ S: "a\x09b",
+ T: T1,
+ }
+ buf := new(bytes.Buffer)
+ Write(buf, x)
+ t.Logf("\n%s", buf.String())
+ t.Logf("\n%#v", x)
+}
+
+type X struct {
+ Y Y
+ P *Y
+ A []Y
+ F bool
+ S string
+ T T
+}
+
+type Y struct {
+ V int
+}
+
+type T int
+
+const (
+ T0 T = iota
+ T1
+)