...

Source file src/google.golang.org/protobuf/reflect/protodesc/editions.go

Documentation: google.golang.org/protobuf/reflect/protodesc

     1  // Copyright 2019 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 protodesc
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"sync"
    11  
    12  	"google.golang.org/protobuf/internal/editiondefaults"
    13  	"google.golang.org/protobuf/internal/filedesc"
    14  	"google.golang.org/protobuf/proto"
    15  	"google.golang.org/protobuf/reflect/protoreflect"
    16  	"google.golang.org/protobuf/types/descriptorpb"
    17  	gofeaturespb "google.golang.org/protobuf/types/gofeaturespb"
    18  )
    19  
    20  var defaults = &descriptorpb.FeatureSetDefaults{}
    21  var defaultsCacheMu sync.Mutex
    22  var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)
    23  
    24  func init() {
    25  	err := proto.Unmarshal(editiondefaults.Defaults, defaults)
    26  	if err != nil {
    27  		fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
    28  		os.Exit(1)
    29  	}
    30  }
    31  
    32  func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
    33  	return filedesc.Edition(epb)
    34  }
    35  
    36  func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
    37  	switch ed {
    38  	case filedesc.EditionUnknown:
    39  		return descriptorpb.Edition_EDITION_UNKNOWN
    40  	case filedesc.EditionProto2:
    41  		return descriptorpb.Edition_EDITION_PROTO2
    42  	case filedesc.EditionProto3:
    43  		return descriptorpb.Edition_EDITION_PROTO3
    44  	case filedesc.Edition2023:
    45  		return descriptorpb.Edition_EDITION_2023
    46  	default:
    47  		panic(fmt.Sprintf("unknown value for edition: %v", ed))
    48  	}
    49  }
    50  
    51  func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
    52  	defaultsCacheMu.Lock()
    53  	defer defaultsCacheMu.Unlock()
    54  	if def, ok := defaultsCache[ed]; ok {
    55  		return def
    56  	}
    57  	edpb := toEditionProto(ed)
    58  	if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
    59  		// This should never happen protodesc.(FileOptions).New would fail when
    60  		// initializing the file descriptor.
    61  		// This most likely means the embedded defaults were not updated.
    62  		fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb)
    63  		os.Exit(1)
    64  	}
    65  	fsed := defaults.GetDefaults()[0]
    66  	// Using a linear search for now.
    67  	// Editions are guaranteed to be sorted and thus we could use a binary search.
    68  	// Given that there are only a handful of editions (with one more per year)
    69  	// there is not much reason to use a binary search.
    70  	for _, def := range defaults.GetDefaults() {
    71  		if def.GetEdition() <= edpb {
    72  			fsed = def
    73  		} else {
    74  			break
    75  		}
    76  	}
    77  	fs := proto.Clone(fsed.GetFixedFeatures()).(*descriptorpb.FeatureSet)
    78  	proto.Merge(fs, fsed.GetOverridableFeatures())
    79  	defaultsCache[ed] = fs
    80  	return fs
    81  }
    82  
    83  // mergeEditionFeatures merges the parent and child feature sets. This function
    84  // should be used when initializing Go descriptors from descriptor protos which
    85  // is why the parent is a filedesc.EditionsFeatures (Go representation) while
    86  // the child is a descriptorproto.FeatureSet (protoc representation).
    87  // Any feature set by the child overwrites what is set by the parent.
    88  func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorpb.FeatureSet) filedesc.EditionFeatures {
    89  	var parentFS filedesc.EditionFeatures
    90  	switch p := parentDesc.(type) {
    91  	case *filedesc.File:
    92  		parentFS = p.L1.EditionFeatures
    93  	case *filedesc.Message:
    94  		parentFS = p.L1.EditionFeatures
    95  	default:
    96  		panic(fmt.Sprintf("unknown parent type %T", parentDesc))
    97  	}
    98  	if child == nil {
    99  		return parentFS
   100  	}
   101  	if fp := child.FieldPresence; fp != nil {
   102  		parentFS.IsFieldPresence = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
   103  			*fp == descriptorpb.FeatureSet_EXPLICIT
   104  		parentFS.IsLegacyRequired = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED
   105  	}
   106  	if et := child.EnumType; et != nil {
   107  		parentFS.IsOpenEnum = *et == descriptorpb.FeatureSet_OPEN
   108  	}
   109  
   110  	if rfe := child.RepeatedFieldEncoding; rfe != nil {
   111  		parentFS.IsPacked = *rfe == descriptorpb.FeatureSet_PACKED
   112  	}
   113  
   114  	if utf8val := child.Utf8Validation; utf8val != nil {
   115  		parentFS.IsUTF8Validated = *utf8val == descriptorpb.FeatureSet_VERIFY
   116  	}
   117  
   118  	if me := child.MessageEncoding; me != nil {
   119  		parentFS.IsDelimitedEncoded = *me == descriptorpb.FeatureSet_DELIMITED
   120  	}
   121  
   122  	if jf := child.JsonFormat; jf != nil {
   123  		parentFS.IsJSONCompliant = *jf == descriptorpb.FeatureSet_ALLOW
   124  	}
   125  
   126  	if goFeatures, ok := proto.GetExtension(child, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures); ok && goFeatures != nil {
   127  		if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil {
   128  			parentFS.GenerateLegacyUnmarshalJSON = *luje
   129  		}
   130  	}
   131  
   132  	return parentFS
   133  }
   134  
   135  // initFileDescFromFeatureSet initializes editions related fields in fd based
   136  // on fs. If fs is nil it is assumed to be an empty featureset and all fields
   137  // will be initialized with the appropriate default. fd.L1.Edition must be set
   138  // before calling this function.
   139  func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
   140  	dfs := getFeatureSetFor(fd.L1.Edition)
   141  	// initialize the featureset with the defaults
   142  	fd.L1.EditionFeatures = mergeEditionFeatures(fd, dfs)
   143  	// overwrite any options explicitly specified
   144  	fd.L1.EditionFeatures = mergeEditionFeatures(fd, fs)
   145  }
   146  

View as plain text