mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-29 00:32:43 +00:00
e8baad6b6c
This is the first of multiple changes to add protobuf editions support to the Go implementation. This change includes the feature resolutions for protobuf editions. The plugin does not yet report editions support and protoc would fail if it invoked this plugin with a proto that uses editions. Change-Id: I7d31909366c3433b21abab74ec92263e08145434 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/547315 Auto-Submit: Lasse Folger <lassefolger@google.com> Reviewed-by: Michael Stapelberg <stapelberg@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Commit-Queue: Lasse Folger <lassefolger@google.com> Reviewed-by: Lasse Folger <lassefolger@google.com>
178 lines
5.9 KiB
Go
178 lines
5.9 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package protodesc
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
|
|
"google.golang.org/protobuf/internal/filedesc"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/descriptorpb"
|
|
)
|
|
|
|
const (
|
|
SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2
|
|
SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023
|
|
)
|
|
|
|
//go:embed editions_defaults.binpb
|
|
var binaryEditionDefaults []byte
|
|
var defaults = &descriptorpb.FeatureSetDefaults{}
|
|
var defaultsCacheMu sync.Mutex
|
|
var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)
|
|
|
|
func init() {
|
|
err := proto.Unmarshal(binaryEditionDefaults, defaults)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
|
|
return filedesc.Edition(epb)
|
|
}
|
|
|
|
func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
|
|
switch ed {
|
|
case filedesc.EditionUnknown:
|
|
return descriptorpb.Edition_EDITION_UNKNOWN
|
|
case filedesc.EditionProto2:
|
|
return descriptorpb.Edition_EDITION_PROTO2
|
|
case filedesc.EditionProto3:
|
|
return descriptorpb.Edition_EDITION_PROTO3
|
|
case filedesc.Edition2023:
|
|
return descriptorpb.Edition_EDITION_2023
|
|
default:
|
|
panic(fmt.Sprintf("unknown value for edition: %v", ed))
|
|
}
|
|
}
|
|
|
|
func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
|
|
defaultsCacheMu.Lock()
|
|
defer defaultsCacheMu.Unlock()
|
|
if def, ok := defaultsCache[ed]; ok {
|
|
return def
|
|
}
|
|
edpb := toEditionProto(ed)
|
|
if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
|
|
// This should never happen protodesc.(FileOptions).New would fail when
|
|
// initializing the file descriptor.
|
|
// This most likely means the embedded defaults were not updated.
|
|
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)
|
|
os.Exit(1)
|
|
}
|
|
fs := defaults.GetDefaults()[0].GetFeatures()
|
|
// Using a linear search for now.
|
|
// Editions are guaranteed to be sorted and thus we could use a binary search.
|
|
// Given that there are only a handful of editions (with one more per year)
|
|
// there is not much reason to use a binary search.
|
|
for _, def := range defaults.GetDefaults() {
|
|
if def.GetEdition() <= edpb {
|
|
fs = def.GetFeatures()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
defaultsCache[ed] = fs
|
|
return fs
|
|
}
|
|
|
|
func resolveFeatureHasFieldPresence(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
|
|
fs := fieldDesc.GetOptions().GetFeatures()
|
|
if fs == nil || fs.FieldPresence == nil {
|
|
return fileDesc.L1.EditionFeatures.IsFieldPresence
|
|
}
|
|
return fs.GetFieldPresence() == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
|
|
fs.GetFieldPresence() == descriptorpb.FeatureSet_EXPLICIT
|
|
}
|
|
|
|
func resolveFeatureRepeatedFieldEncodingPacked(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
|
|
fs := fieldDesc.GetOptions().GetFeatures()
|
|
if fs == nil || fs.RepeatedFieldEncoding == nil {
|
|
return fileDesc.L1.EditionFeatures.IsPacked
|
|
}
|
|
return fs.GetRepeatedFieldEncoding() == descriptorpb.FeatureSet_PACKED
|
|
}
|
|
|
|
func resolveFeatureEnforceUTF8(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
|
|
fs := fieldDesc.GetOptions().GetFeatures()
|
|
if fs == nil || fs.Utf8Validation == nil {
|
|
return fileDesc.L1.EditionFeatures.IsUTF8Validated
|
|
}
|
|
return fs.GetUtf8Validation() == descriptorpb.FeatureSet_VERIFY
|
|
}
|
|
|
|
func resolveFeatureDelimitedEncoding(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
|
|
fs := fieldDesc.GetOptions().GetFeatures()
|
|
if fs == nil || fs.MessageEncoding == nil {
|
|
return fileDesc.L1.EditionFeatures.IsDelimitedEncoded
|
|
}
|
|
return fs.GetMessageEncoding() == descriptorpb.FeatureSet_DELIMITED
|
|
}
|
|
|
|
// initFileDescFromFeatureSet initializes editions related fields in fd based
|
|
// on fs. If fs is nil it is assumed to be an empty featureset and all fields
|
|
// will be initialized with the appropriate default. fd.L1.Edition must be set
|
|
// before calling this function.
|
|
func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
|
|
dfs := getFeatureSetFor(fd.L1.Edition)
|
|
if fs == nil {
|
|
fs = &descriptorpb.FeatureSet{}
|
|
}
|
|
|
|
var fieldPresence descriptorpb.FeatureSet_FieldPresence
|
|
if fp := fs.FieldPresence; fp != nil {
|
|
fieldPresence = *fp
|
|
} else {
|
|
fieldPresence = *dfs.FieldPresence
|
|
}
|
|
fd.L1.EditionFeatures.IsFieldPresence = fieldPresence == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
|
|
fieldPresence == descriptorpb.FeatureSet_EXPLICIT
|
|
|
|
var enumType descriptorpb.FeatureSet_EnumType
|
|
if et := fs.EnumType; et != nil {
|
|
enumType = *et
|
|
} else {
|
|
enumType = *dfs.EnumType
|
|
}
|
|
fd.L1.EditionFeatures.IsOpenEnum = enumType == descriptorpb.FeatureSet_OPEN
|
|
|
|
var respeatedFieldEncoding descriptorpb.FeatureSet_RepeatedFieldEncoding
|
|
if rfe := fs.RepeatedFieldEncoding; rfe != nil {
|
|
respeatedFieldEncoding = *rfe
|
|
} else {
|
|
respeatedFieldEncoding = *dfs.RepeatedFieldEncoding
|
|
}
|
|
fd.L1.EditionFeatures.IsPacked = respeatedFieldEncoding == descriptorpb.FeatureSet_PACKED
|
|
|
|
var isUTF8Validated descriptorpb.FeatureSet_Utf8Validation
|
|
if utf8val := fs.Utf8Validation; utf8val != nil {
|
|
isUTF8Validated = *utf8val
|
|
} else {
|
|
isUTF8Validated = *dfs.Utf8Validation
|
|
}
|
|
fd.L1.EditionFeatures.IsUTF8Validated = isUTF8Validated == descriptorpb.FeatureSet_VERIFY
|
|
|
|
var messageEncoding descriptorpb.FeatureSet_MessageEncoding
|
|
if me := fs.MessageEncoding; me != nil {
|
|
messageEncoding = *me
|
|
} else {
|
|
messageEncoding = *dfs.MessageEncoding
|
|
}
|
|
fd.L1.EditionFeatures.IsDelimitedEncoded = messageEncoding == descriptorpb.FeatureSet_DELIMITED
|
|
|
|
var jsonFormat descriptorpb.FeatureSet_JsonFormat
|
|
if jf := fs.JsonFormat; jf != nil {
|
|
jsonFormat = *jf
|
|
} else {
|
|
jsonFormat = *dfs.JsonFormat
|
|
}
|
|
fd.L1.EditionFeatures.IsJSONCompliant = jsonFormat == descriptorpb.FeatureSet_ALLOW
|
|
}
|