github.com/RomiChan/protobuf@v0.1.1-0.20230204044148-2ed269a2e54d/internal/generator/init.go (about)

     1  // protocolbuffers/protobuf-go cmd/protoc-gen-go/internal_gengo/init.go
     2  // https://github.com/protocolbuffers/protobuf-go/blob/master/cmd/protoc-gen-go/internal_gengo/init.go
     3  //
     4  //	Copyright © 2019 The Go Authors. All rights reserved.
     5  //	Portions Copyright © 2021 RomiChan
     6  //
     7  // Redistribution and use in source and binary forms, with or without
     8  // modification, are permitted provided that the following conditions are
     9  // met:
    10  //
    11  // * Redistributions of source code must retain the above copyright
    12  // notice, this list of conditions and the following disclaimer.
    13  // * Redistributions in binary form must reproduce the above
    14  // copyright notice, this list of conditions and the following disclaimer
    15  // in the documentation and/or other materials provided with the
    16  // distribution.
    17  // * Neither the name of Google Inc. nor the names of its
    18  // contributors may be used to endorse or promote products derived from
    19  // this software without specific prior written permission.
    20  //
    21  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    22  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    23  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    24  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    25  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    26  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    27  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    28  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    29  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    30  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    31  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    32  
    33  package generator
    34  
    35  import (
    36  	"unicode"
    37  	"unicode/utf8"
    38  
    39  	"google.golang.org/protobuf/compiler/protogen"
    40  	"google.golang.org/protobuf/encoding/protowire"
    41  	"google.golang.org/protobuf/types/descriptorpb"
    42  )
    43  
    44  type fileInfo struct {
    45  	*protogen.File
    46  
    47  	allEnums      []*enumInfo
    48  	allMessages   []*messageInfo
    49  	allExtensions []*extensionInfo
    50  
    51  	allEnumsByPtr         map[*enumInfo]int    // value is index into allEnums
    52  	allMessagesByPtr      map[*messageInfo]int // value is index into allMessages
    53  	allMessageFieldsByPtr map[*messageInfo]*structFields
    54  
    55  	comparable bool
    56  }
    57  
    58  type structFields struct {
    59  	count      int
    60  	unexported map[int]string
    61  }
    62  
    63  func (sf *structFields) append(name string) {
    64  	if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) {
    65  		if sf.unexported == nil {
    66  			sf.unexported = make(map[int]string)
    67  		}
    68  		sf.unexported[sf.count] = name
    69  	}
    70  	sf.count++
    71  }
    72  
    73  func newFileInfo(file *protogen.File) *fileInfo {
    74  	f := &fileInfo{File: file}
    75  
    76  	// Collect all enums, messages, and extensions in "flattened ordering".
    77  	// See filetype.TypeBuilder.
    78  	var walkMessages func([]*protogen.Message, func(*protogen.Message))
    79  	walkMessages = func(messages []*protogen.Message, f func(*protogen.Message)) {
    80  		for _, m := range messages {
    81  			f(m)
    82  			walkMessages(m.Messages, f)
    83  		}
    84  	}
    85  	initEnumInfos := func(enums []*protogen.Enum) {
    86  		for _, enum := range enums {
    87  			f.allEnums = append(f.allEnums, newEnumInfo(f, enum))
    88  		}
    89  	}
    90  	initMessageInfos := func(messages []*protogen.Message) {
    91  		for _, message := range messages {
    92  			f.allMessages = append(f.allMessages, newMessageInfo(f, message))
    93  		}
    94  	}
    95  	initExtensionInfos := func(extensions []*protogen.Extension) {
    96  		for _, extension := range extensions {
    97  			f.allExtensions = append(f.allExtensions, newExtensionInfo(f, extension))
    98  		}
    99  	}
   100  	initEnumInfos(f.Enums)
   101  	initMessageInfos(f.Messages)
   102  	initExtensionInfos(f.Extensions)
   103  	walkMessages(f.Messages, func(m *protogen.Message) {
   104  		initEnumInfos(m.Enums)
   105  		initMessageInfos(m.Messages)
   106  		initExtensionInfos(m.Extensions)
   107  	})
   108  
   109  	// Derive a reverse mapping of enum and message pointers to their index
   110  	// in allEnums and allMessages.
   111  	if len(f.allEnums) > 0 {
   112  		f.allEnumsByPtr = make(map[*enumInfo]int)
   113  		for i, e := range f.allEnums {
   114  			f.allEnumsByPtr[e] = i
   115  		}
   116  	}
   117  	if len(f.allMessages) > 0 {
   118  		f.allMessagesByPtr = make(map[*messageInfo]int)
   119  		f.allMessageFieldsByPtr = make(map[*messageInfo]*structFields)
   120  		for i, m := range f.allMessages {
   121  			f.allMessagesByPtr[m] = i
   122  			f.allMessageFieldsByPtr[m] = new(structFields)
   123  		}
   124  	}
   125  
   126  	return f
   127  }
   128  
   129  type enumInfo struct {
   130  	*protogen.Enum
   131  }
   132  
   133  func newEnumInfo(_ *fileInfo, enum *protogen.Enum) *enumInfo {
   134  	e := &enumInfo{Enum: enum}
   135  	return e
   136  }
   137  
   138  type messageInfo struct {
   139  	*protogen.Message
   140  
   141  	isTracked bool
   142  }
   143  
   144  func newMessageInfo(_ *fileInfo, message *protogen.Message) *messageInfo {
   145  	m := &messageInfo{Message: message}
   146  	m.isTracked = isTrackedMessage(m)
   147  	return m
   148  }
   149  
   150  // isTrackedMessage reports whether field tracking is enabled on the message.
   151  func isTrackedMessage(m *messageInfo) (tracked bool) {
   152  	const trackFieldUseFieldNumber = 37383685
   153  
   154  	// Decode the option from unknown fields to avoid a dependency on the
   155  	// annotation proto from protoc-gen-go.
   156  	b := m.Desc.Options().(*descriptorpb.MessageOptions).ProtoReflect().GetUnknown()
   157  	for len(b) > 0 {
   158  		num, typ, n := protowire.ConsumeTag(b)
   159  		b = b[n:]
   160  		if num == trackFieldUseFieldNumber && typ == protowire.VarintType {
   161  			v, _ := protowire.ConsumeVarint(b)
   162  			tracked = protowire.DecodeBool(v)
   163  		}
   164  		m := protowire.ConsumeFieldValue(num, typ, b)
   165  		b = b[m:]
   166  	}
   167  	return tracked
   168  }
   169  
   170  type extensionInfo struct {
   171  	*protogen.Extension
   172  }
   173  
   174  func newExtensionInfo(_ *fileInfo, extension *protogen.Extension) *extensionInfo {
   175  	x := &extensionInfo{Extension: extension}
   176  	return x
   177  }