blob: 2ffa2074df24fe20199b221c5517e06142a54c8a [file] [log] [blame]
Joe Tsaifa02f4e2018-09-12 16:20:37 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package impl
6
7import (
8 "reflect"
9 "strconv"
10 "strings"
11
Joe Tsai01ab2962018-09-21 17:44:00 -070012 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
Joe Tsaifa02f4e2018-09-12 16:20:37 -070013)
14
15type MessageInfo struct {
16 // TODO: Split fields into dense and sparse maps similar to the current
17 // table-driven implementation in v1?
18 fields map[pref.FieldNumber]*fieldInfo
19}
20
21// generateFieldFuncs generates per-field functions for all common operations
22// to be performed on each field. It takes in a reflect.Type representing the
23// Go struct, and a protoreflect.MessageDescriptor to match with the fields
24// in the struct.
25//
26// This code assumes that the struct is well-formed and panics if there are
27// any discrepancies.
28func (mi *MessageInfo) generateFieldFuncs(t reflect.Type, md pref.MessageDescriptor) {
29 // Generate a mapping of field numbers and names to Go struct field or type.
30 fields := map[pref.FieldNumber]reflect.StructField{}
31 oneofs := map[pref.Name]reflect.StructField{}
32 oneofFields := map[pref.FieldNumber]reflect.Type{}
33 special := map[string]reflect.StructField{}
34fieldLoop:
35 for i := 0; i < t.NumField(); i++ {
36 f := t.Field(i)
37 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
38 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
39 n, _ := strconv.ParseUint(s, 10, 64)
40 fields[pref.FieldNumber(n)] = f
41 continue fieldLoop
42 }
43 }
44 if s := f.Tag.Get("protobuf_oneof"); len(s) > 0 {
45 oneofs[pref.Name(s)] = f
46 continue fieldLoop
47 }
48 switch f.Name {
49 case "XXX_weak", "XXX_unrecognized", "XXX_sizecache", "XXX_extensions", "XXX_InternalExtensions":
50 special[f.Name] = f
51 continue fieldLoop
52 }
53 }
54 if fn, ok := t.MethodByName("XXX_OneofFuncs"); ok {
55 vs := fn.Func.Call([]reflect.Value{reflect.New(fn.Type.In(0)).Elem()})[3]
56 oneofLoop:
57 for _, v := range vs.Interface().([]interface{}) {
58 tf := reflect.TypeOf(v).Elem()
59 f := tf.Field(0)
60 for _, s := range strings.Split(f.Tag.Get("protobuf"), ",") {
61 if len(s) > 0 && strings.Trim(s, "0123456789") == "" {
62 n, _ := strconv.ParseUint(s, 10, 64)
63 oneofFields[pref.FieldNumber(n)] = tf
64 continue oneofLoop
65 }
66 }
67 }
68 }
69
70 mi.fields = map[pref.FieldNumber]*fieldInfo{}
71 for i := 0; i < md.Fields().Len(); i++ {
72 fd := md.Fields().Get(i)
73 fs := fields[fd.Number()]
74 var fi fieldInfo
75 switch {
76 case fd.IsWeak():
77 fi = fieldInfoForWeak(fd, special["XXX_weak"])
78 case fd.OneofType() != nil:
79 fi = fieldInfoForOneof(fd, oneofs[fd.OneofType().Name()], oneofFields[fd.Number()])
80 case fd.IsMap():
81 fi = fieldInfoForMap(fd, fs)
82 case fd.Cardinality() == pref.Repeated:
83 fi = fieldInfoForVector(fd, fs)
84 case fd.Kind() != pref.MessageKind && fd.Kind() != pref.GroupKind:
85 fi = fieldInfoForScalar(fd, fs)
86 default:
87 fi = fieldInfoForMessage(fd, fs)
88 }
89 mi.fields[fd.Number()] = &fi
90 }
91}