github.com/whiteCcinn/protobuf-go@v1.0.9/cmd/protoc-gen-go/internal_gengo/reflect.go (about)

     1  // 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  
     5  package internal_gengo
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"strings"
    11  	"unicode/utf8"
    12  
    13  	"github.com/whiteCcinn/protobuf-go/compiler/protogen"
    14  	"github.com/whiteCcinn/protobuf-go/proto"
    15  	"github.com/whiteCcinn/protobuf-go/reflect/protoreflect"
    16  
    17  	"github.com/whiteCcinn/protobuf-go/types/descriptorpb"
    18  )
    19  
    20  func genReflectFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
    21  	g.P("var ", f.GoDescriptorIdent, " ", protoreflectPackage.Ident("FileDescriptor"))
    22  	g.P()
    23  
    24  	genFileDescriptor(gen, g, f)
    25  	if len(f.allEnums) > 0 {
    26  		g.P("var ", enumTypesVarName(f), " = make([]", protoimplPackage.Ident("EnumInfo"), ",", len(f.allEnums), ")")
    27  	}
    28  	if len(f.allMessages) > 0 {
    29  		g.P("var ", messageTypesVarName(f), " = make([]", protoimplPackage.Ident("MessageInfo"), ",", len(f.allMessages), ")")
    30  	}
    31  
    32  	// Generate a unique list of Go types for all declarations and dependencies,
    33  	// and the associated index into the type list for all dependencies.
    34  	var goTypes []string
    35  	var depIdxs []string
    36  	seen := map[protoreflect.FullName]int{}
    37  	genDep := func(name protoreflect.FullName, depSource string) {
    38  		if depSource != "" {
    39  			line := fmt.Sprintf("%d, // %d: %s -> %s", seen[name], len(depIdxs), depSource, name)
    40  			depIdxs = append(depIdxs, line)
    41  		}
    42  	}
    43  	genEnum := func(e *protogen.Enum, depSource string) {
    44  		if e != nil {
    45  			name := e.Desc.FullName()
    46  			if _, ok := seen[name]; !ok {
    47  				line := fmt.Sprintf("(%s)(0), // %d: %s", g.QualifiedGoIdent(e.GoIdent), len(goTypes), name)
    48  				goTypes = append(goTypes, line)
    49  				seen[name] = len(seen)
    50  			}
    51  			if depSource != "" {
    52  				genDep(name, depSource)
    53  			}
    54  		}
    55  	}
    56  	genMessage := func(m *protogen.Message, depSource string) {
    57  		if m != nil {
    58  			name := m.Desc.FullName()
    59  			if _, ok := seen[name]; !ok {
    60  				line := fmt.Sprintf("(*%s)(nil), // %d: %s", g.QualifiedGoIdent(m.GoIdent), len(goTypes), name)
    61  				if m.Desc.IsMapEntry() {
    62  					// Map entry messages have no associated Go type.
    63  					line = fmt.Sprintf("nil, // %d: %s", len(goTypes), name)
    64  				}
    65  				goTypes = append(goTypes, line)
    66  				seen[name] = len(seen)
    67  			}
    68  			if depSource != "" {
    69  				genDep(name, depSource)
    70  			}
    71  		}
    72  	}
    73  
    74  	// This ordering is significant.
    75  	// See filetype.TypeBuilder.DependencyIndexes.
    76  	type offsetEntry struct {
    77  		start int
    78  		name  string
    79  	}
    80  	var depOffsets []offsetEntry
    81  	for _, enum := range f.allEnums {
    82  		genEnum(enum.Enum, "")
    83  	}
    84  	for _, message := range f.allMessages {
    85  		genMessage(message.Message, "")
    86  	}
    87  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "field type_name"})
    88  	for _, message := range f.allMessages {
    89  		for _, field := range message.Fields {
    90  			if field.Desc.IsWeak() {
    91  				continue
    92  			}
    93  			source := string(field.Desc.FullName())
    94  			genEnum(field.Enum, source+":type_name")
    95  			genMessage(field.Message, source+":type_name")
    96  		}
    97  	}
    98  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension extendee"})
    99  	for _, extension := range f.allExtensions {
   100  		source := string(extension.Desc.FullName())
   101  		genMessage(extension.Extendee, source+":extendee")
   102  	}
   103  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "extension type_name"})
   104  	for _, extension := range f.allExtensions {
   105  		source := string(extension.Desc.FullName())
   106  		genEnum(extension.Enum, source+":type_name")
   107  		genMessage(extension.Message, source+":type_name")
   108  	}
   109  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method input_type"})
   110  	for _, service := range f.Services {
   111  		for _, method := range service.Methods {
   112  			source := string(method.Desc.FullName())
   113  			genMessage(method.Input, source+":input_type")
   114  		}
   115  	}
   116  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), "method output_type"})
   117  	for _, service := range f.Services {
   118  		for _, method := range service.Methods {
   119  			source := string(method.Desc.FullName())
   120  			genMessage(method.Output, source+":output_type")
   121  		}
   122  	}
   123  	depOffsets = append(depOffsets, offsetEntry{len(depIdxs), ""})
   124  	for i := len(depOffsets) - 2; i >= 0; i-- {
   125  		curr, next := depOffsets[i], depOffsets[i+1]
   126  		depIdxs = append(depIdxs, fmt.Sprintf("%d, // [%d:%d] is the sub-list for %s",
   127  			curr.start, curr.start, next.start, curr.name))
   128  	}
   129  	if len(depIdxs) > math.MaxInt32 {
   130  		panic("too many dependencies") // sanity check
   131  	}
   132  
   133  	g.P("var ", goTypesVarName(f), " = []interface{}{")
   134  	for _, s := range goTypes {
   135  		g.P(s)
   136  	}
   137  	g.P("}")
   138  
   139  	g.P("var ", depIdxsVarName(f), " = []int32{")
   140  	for _, s := range depIdxs {
   141  		g.P(s)
   142  	}
   143  	g.P("}")
   144  
   145  	g.P("func init() { ", initFuncName(f.File), "() }")
   146  
   147  	g.P("func ", initFuncName(f.File), "() {")
   148  	g.P("if ", f.GoDescriptorIdent, " != nil {")
   149  	g.P("return")
   150  	g.P("}")
   151  
   152  	// Ensure that initialization functions for different files in the same Go
   153  	// package run in the correct order: Call the init funcs for every .proto file
   154  	// imported by this one that is in the same Go package.
   155  	for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
   156  		impFile := gen.FilesByPath[imps.Get(i).Path()]
   157  		if impFile.GoImportPath != f.GoImportPath {
   158  			continue
   159  		}
   160  		g.P(initFuncName(impFile), "()")
   161  	}
   162  
   163  	if len(f.allMessages) > 0 {
   164  		// Populate MessageInfo.Exporters.
   165  		g.P("if !", protoimplPackage.Ident("UnsafeEnabled"), " {")
   166  		for _, message := range f.allMessages {
   167  			if sf := f.allMessageFieldsByPtr[message]; len(sf.unexported) > 0 {
   168  				idx := f.allMessagesByPtr[message]
   169  				typesVar := messageTypesVarName(f)
   170  
   171  				g.P(typesVar, "[", idx, "].Exporter = func(v interface{}, i int) interface{} {")
   172  				g.P("switch v := v.(*", message.GoIdent, "); i {")
   173  				for i := 0; i < sf.count; i++ {
   174  					if name := sf.unexported[i]; name != "" {
   175  						g.P("case ", i, ": return &v.", name)
   176  					}
   177  				}
   178  				g.P("default: return nil")
   179  				g.P("}")
   180  				g.P("}")
   181  			}
   182  		}
   183  		g.P("}")
   184  
   185  		// Populate MessageInfo.OneofWrappers.
   186  		for _, message := range f.allMessages {
   187  			if len(message.Oneofs) > 0 {
   188  				idx := f.allMessagesByPtr[message]
   189  				typesVar := messageTypesVarName(f)
   190  
   191  				// Associate the wrapper types by directly passing them to the MessageInfo.
   192  				g.P(typesVar, "[", idx, "].OneofWrappers = []interface{} {")
   193  				for _, oneof := range message.Oneofs {
   194  					if !oneof.Desc.IsSynthetic() {
   195  						for _, field := range oneof.Fields {
   196  							g.P("(*", field.GoIdent, ")(nil),")
   197  						}
   198  					}
   199  				}
   200  				g.P("}")
   201  			}
   202  		}
   203  	}
   204  
   205  	g.P("type x struct{}")
   206  	g.P("out := ", protoimplPackage.Ident("TypeBuilder"), "{")
   207  	g.P("File: ", protoimplPackage.Ident("DescBuilder"), "{")
   208  	g.P("GoPackagePath: ", reflectPackage.Ident("TypeOf"), "(x{}).PkgPath(),")
   209  	g.P("RawDescriptor: ", rawDescVarName(f), ",")
   210  	g.P("NumEnums: ", len(f.allEnums), ",")
   211  	g.P("NumMessages: ", len(f.allMessages), ",")
   212  	g.P("NumExtensions: ", len(f.allExtensions), ",")
   213  	g.P("NumServices: ", len(f.Services), ",")
   214  	g.P("},")
   215  	g.P("GoTypes: ", goTypesVarName(f), ",")
   216  	g.P("DependencyIndexes: ", depIdxsVarName(f), ",")
   217  	if len(f.allEnums) > 0 {
   218  		g.P("EnumInfos: ", enumTypesVarName(f), ",")
   219  	}
   220  	if len(f.allMessages) > 0 {
   221  		g.P("MessageInfos: ", messageTypesVarName(f), ",")
   222  	}
   223  	if len(f.allExtensions) > 0 {
   224  		g.P("ExtensionInfos: ", extensionTypesVarName(f), ",")
   225  	}
   226  	g.P("}.Build()")
   227  	g.P(f.GoDescriptorIdent, " = out.File")
   228  
   229  	// Set inputs to nil to allow GC to reclaim resources.
   230  	g.P(rawDescVarName(f), " = nil")
   231  	g.P(goTypesVarName(f), " = nil")
   232  	g.P(depIdxsVarName(f), " = nil")
   233  	g.P("}")
   234  }
   235  
   236  func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
   237  	descProto := proto.Clone(f.Proto).(*descriptorpb.FileDescriptorProto)
   238  	descProto.SourceCodeInfo = nil // drop source code information
   239  
   240  	b, err := proto.MarshalOptions{AllowPartial: true, Deterministic: true}.Marshal(descProto)
   241  	if err != nil {
   242  		gen.Error(err)
   243  		return
   244  	}
   245  
   246  	g.P("var ", rawDescVarName(f), " = []byte{")
   247  	for len(b) > 0 {
   248  		n := 16
   249  		if n > len(b) {
   250  			n = len(b)
   251  		}
   252  
   253  		s := ""
   254  		for _, c := range b[:n] {
   255  			s += fmt.Sprintf("0x%02x,", c)
   256  		}
   257  		g.P(s)
   258  
   259  		b = b[n:]
   260  	}
   261  	g.P("}")
   262  	g.P()
   263  
   264  	if f.needRawDesc {
   265  		onceVar := rawDescVarName(f) + "Once"
   266  		dataVar := rawDescVarName(f) + "Data"
   267  		g.P("var (")
   268  		g.P(onceVar, " ", syncPackage.Ident("Once"))
   269  		g.P(dataVar, " = ", rawDescVarName(f))
   270  		g.P(")")
   271  		g.P()
   272  
   273  		g.P("func ", rawDescVarName(f), "GZIP() []byte {")
   274  		g.P(onceVar, ".Do(func() {")
   275  		g.P(dataVar, " = ", protoimplPackage.Ident("X"), ".CompressGZIP(", dataVar, ")")
   276  		g.P("})")
   277  		g.P("return ", dataVar)
   278  		g.P("}")
   279  		g.P()
   280  	}
   281  }
   282  
   283  func genEnumReflectMethods(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
   284  	idx := f.allEnumsByPtr[e]
   285  	typesVar := enumTypesVarName(f)
   286  
   287  	// Descriptor method.
   288  	g.P("func (", e.GoIdent, ") Descriptor() ", protoreflectPackage.Ident("EnumDescriptor"), " {")
   289  	g.P("return ", typesVar, "[", idx, "].Descriptor()")
   290  	g.P("}")
   291  	g.P()
   292  
   293  	// Type method.
   294  	g.P("func (", e.GoIdent, ") Type() ", protoreflectPackage.Ident("EnumType"), " {")
   295  	g.P("return &", typesVar, "[", idx, "]")
   296  	g.P("}")
   297  	g.P()
   298  
   299  	// Number method.
   300  	g.P("func (x ", e.GoIdent, ") Number() ", protoreflectPackage.Ident("EnumNumber"), " {")
   301  	g.P("return ", protoreflectPackage.Ident("EnumNumber"), "(x)")
   302  	g.P("}")
   303  	g.P()
   304  }
   305  
   306  func genMessageReflectMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   307  	idx := f.allMessagesByPtr[m]
   308  	typesVar := messageTypesVarName(f)
   309  
   310  	// ProtoReflect method.
   311  	g.P("func (x *", m.GoIdent, ") ProtoReflect() ", protoreflectPackage.Ident("Message"), " {")
   312  	g.P("mi := &", typesVar, "[", idx, "]")
   313  	g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " && x != nil {")
   314  	g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
   315  	g.P("if ms.LoadMessageInfo() == nil {")
   316  	g.P("ms.StoreMessageInfo(mi)")
   317  	g.P("}")
   318  	g.P("return ms")
   319  	g.P("}")
   320  	g.P("return mi.MessageOf(x)")
   321  	g.P("}")
   322  	g.P()
   323  }
   324  
   325  func fileVarName(f *protogen.File, suffix string) string {
   326  	prefix := f.GoDescriptorIdent.GoName
   327  	_, n := utf8.DecodeRuneInString(prefix)
   328  	prefix = strings.ToLower(prefix[:n]) + prefix[n:]
   329  	return prefix + "_" + suffix
   330  }
   331  func rawDescVarName(f *fileInfo) string {
   332  	return fileVarName(f.File, "rawDesc")
   333  }
   334  func goTypesVarName(f *fileInfo) string {
   335  	return fileVarName(f.File, "goTypes")
   336  }
   337  func depIdxsVarName(f *fileInfo) string {
   338  	return fileVarName(f.File, "depIdxs")
   339  }
   340  func enumTypesVarName(f *fileInfo) string {
   341  	return fileVarName(f.File, "enumTypes")
   342  }
   343  func messageTypesVarName(f *fileInfo) string {
   344  	return fileVarName(f.File, "msgTypes")
   345  }
   346  func extensionTypesVarName(f *fileInfo) string {
   347  	return fileVarName(f.File, "extTypes")
   348  }
   349  func initFuncName(f *protogen.File) string {
   350  	return fileVarName(f, "init")
   351  }