2018-10-18 11:06:29 -07:00
|
|
|
// Copyright 2018 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.
|
|
|
|
|
2018-11-26 22:32:06 -08:00
|
|
|
package impl_test
|
2018-10-18 11:06:29 -07:00
|
|
|
|
|
|
|
import (
|
2018-10-25 14:06:56 -07:00
|
|
|
"bytes"
|
|
|
|
"math"
|
2018-10-18 11:06:29 -07:00
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
2018-11-27 17:25:04 -08:00
|
|
|
papi "github.com/golang/protobuf/protoapi"
|
2018-11-11 17:56:21 -08:00
|
|
|
pack "github.com/golang/protobuf/v2/internal/encoding/pack"
|
2018-11-26 22:32:06 -08:00
|
|
|
pimpl "github.com/golang/protobuf/v2/internal/impl"
|
2018-11-11 17:56:21 -08:00
|
|
|
pragma "github.com/golang/protobuf/v2/internal/pragma"
|
2019-03-13 12:56:39 -07:00
|
|
|
ptype "github.com/golang/protobuf/v2/internal/prototype"
|
2018-11-27 18:45:07 -08:00
|
|
|
scalar "github.com/golang/protobuf/v2/internal/scalar"
|
2018-10-18 11:06:29 -07:00
|
|
|
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
|
2018-11-11 17:56:21 -08:00
|
|
|
cmp "github.com/google/go-cmp/cmp"
|
2018-11-26 22:32:06 -08:00
|
|
|
cmpopts "github.com/google/go-cmp/cmp/cmpopts"
|
|
|
|
|
|
|
|
// The legacy package must be imported prior to use of any legacy messages.
|
|
|
|
// TODO: Remove this when protoV1 registers these hooks for you.
|
|
|
|
plegacy "github.com/golang/protobuf/v2/internal/legacy"
|
2018-11-11 17:56:21 -08:00
|
|
|
|
|
|
|
proto2_20180125 "github.com/golang/protobuf/v2/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
|
2018-10-18 11:06:29 -07:00
|
|
|
)
|
|
|
|
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
type legacyTestMessage struct {
|
2018-10-31 18:23:42 -07:00
|
|
|
XXX_unrecognized []byte
|
2018-11-27 17:25:04 -08:00
|
|
|
papi.XXX_InternalExtensions
|
2018-10-31 18:23:42 -07:00
|
|
|
}
|
|
|
|
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
func (*legacyTestMessage) Reset() {}
|
|
|
|
func (*legacyTestMessage) String() string { return "" }
|
|
|
|
func (*legacyTestMessage) ProtoMessage() {}
|
2018-11-27 17:25:04 -08:00
|
|
|
func (*legacyTestMessage) ExtensionRangeArray() []papi.ExtensionRange {
|
|
|
|
return []papi.ExtensionRange{{Start: 10, End: 20}, {Start: 40, End: 80}, {Start: 10000, End: 20000}}
|
2018-10-31 18:23:42 -07:00
|
|
|
}
|
|
|
|
|
2018-10-25 14:06:56 -07:00
|
|
|
func TestLegacyUnknown(t *testing.T) {
|
|
|
|
rawOf := func(toks ...pack.Token) pref.RawFields {
|
|
|
|
return pref.RawFields(pack.Message(toks).Marshal())
|
|
|
|
}
|
|
|
|
raw1a := rawOf(pack.Tag{1, pack.VarintType}, pack.Svarint(-4321)) // 08c143
|
|
|
|
raw1b := rawOf(pack.Tag{1, pack.Fixed32Type}, pack.Uint32(0xdeadbeef)) // 0defbeadde
|
|
|
|
raw1c := rawOf(pack.Tag{1, pack.Fixed64Type}, pack.Float64(math.Pi)) // 09182d4454fb210940
|
|
|
|
raw2a := rawOf(pack.Tag{2, pack.BytesType}, pack.String("hello, world!")) // 120d68656c6c6f2c20776f726c6421
|
|
|
|
raw2b := rawOf(pack.Tag{2, pack.VarintType}, pack.Uvarint(1234)) // 10d209
|
|
|
|
raw3a := rawOf(pack.Tag{3, pack.StartGroupType}, pack.Tag{3, pack.EndGroupType}) // 1b1c
|
|
|
|
raw3b := rawOf(pack.Tag{3, pack.BytesType}, pack.Bytes("\xde\xad\xbe\xef")) // 1a04deadbeef
|
|
|
|
|
2018-10-31 18:23:42 -07:00
|
|
|
raw1 := rawOf(pack.Tag{1, pack.BytesType}, pack.Bytes("1")) // 0a0131
|
|
|
|
raw3 := rawOf(pack.Tag{3, pack.BytesType}, pack.Bytes("3")) // 1a0133
|
|
|
|
raw10 := rawOf(pack.Tag{10, pack.BytesType}, pack.Bytes("10")) // 52023130 - extension
|
|
|
|
raw15 := rawOf(pack.Tag{15, pack.BytesType}, pack.Bytes("15")) // 7a023135 - extension
|
|
|
|
raw26 := rawOf(pack.Tag{26, pack.BytesType}, pack.Bytes("26")) // d201023236
|
|
|
|
raw32 := rawOf(pack.Tag{32, pack.BytesType}, pack.Bytes("32")) // 8202023332
|
|
|
|
raw45 := rawOf(pack.Tag{45, pack.BytesType}, pack.Bytes("45")) // ea02023435 - extension
|
|
|
|
raw46 := rawOf(pack.Tag{45, pack.BytesType}, pack.Bytes("46")) // ea02023436 - extension
|
|
|
|
raw47 := rawOf(pack.Tag{45, pack.BytesType}, pack.Bytes("47")) // ea02023437 - extension
|
|
|
|
raw99 := rawOf(pack.Tag{99, pack.BytesType}, pack.Bytes("99")) // 9a06023939
|
|
|
|
|
2018-10-25 14:06:56 -07:00
|
|
|
joinRaw := func(bs ...pref.RawFields) (out []byte) {
|
|
|
|
for _, b := range bs {
|
|
|
|
out = append(out, b...)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
m := new(legacyTestMessage)
|
2018-11-26 22:32:06 -08:00
|
|
|
fs := pimpl.Export{}.MessageOf(m).UnknownFields()
|
2018-10-31 18:23:42 -07:00
|
|
|
|
2018-10-25 14:06:56 -07:00
|
|
|
if got, want := fs.Len(), 0; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(1, raw1a)
|
|
|
|
fs.Set(1, append(fs.Get(1), raw1b...))
|
|
|
|
fs.Set(1, append(fs.Get(1), raw1c...))
|
|
|
|
if got, want := fs.Len(), 1; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw1a, raw1b, raw1c); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(2, raw2a)
|
|
|
|
if got, want := fs.Len(), 2; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw1a, raw1b, raw1c, raw2a); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := fs.Get(1), joinRaw(raw1a, raw1b, raw1c); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("Get(%d) = %x, want %x", 1, got, want)
|
|
|
|
}
|
|
|
|
if got, want := fs.Get(2), joinRaw(raw2a); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("Get(%d) = %x, want %x", 2, got, want)
|
|
|
|
}
|
|
|
|
if got, want := fs.Get(3), joinRaw(); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("Get(%d) = %x, want %x", 3, got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(1, nil) // remove field 1
|
|
|
|
if got, want := fs.Len(), 1; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw2a); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simulate manual appending of raw field data.
|
2018-10-31 18:23:42 -07:00
|
|
|
m.XXX_unrecognized = append(m.XXX_unrecognized, joinRaw(raw3a, raw1a, raw1b, raw2b, raw3b, raw1c)...)
|
2018-10-25 14:06:56 -07:00
|
|
|
if got, want := fs.Len(), 3; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify range iteration order.
|
|
|
|
var i int
|
|
|
|
want := []struct {
|
|
|
|
num pref.FieldNumber
|
|
|
|
raw pref.RawFields
|
|
|
|
}{
|
|
|
|
{2, joinRaw(raw2a, raw2b)},
|
2018-10-29 02:10:42 -07:00
|
|
|
{3, joinRaw(raw3a, raw3b)},
|
2018-10-25 14:06:56 -07:00
|
|
|
{1, joinRaw(raw1a, raw1b, raw1c)},
|
|
|
|
}
|
|
|
|
fs.Range(func(num pref.FieldNumber, raw pref.RawFields) bool {
|
|
|
|
if i < len(want) {
|
|
|
|
if num != want[i].num || !bytes.Equal(raw, want[i].raw) {
|
|
|
|
t.Errorf("Range(%d) = (%d, %x), want (%d, %x)", i, num, raw, want[i].num, want[i].raw)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Errorf("unexpected Range iteration: %d", i)
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
fs.Set(2, fs.Get(2)) // moves field 2 to the end
|
|
|
|
if got, want := fs.Len(), 3; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw3a, raw1a, raw1b, raw3b, raw1c, raw2a, raw2b); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
fs.Set(1, nil) // remove field 1
|
|
|
|
if got, want := fs.Len(), 2; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw3a, raw3b, raw2a, raw2b); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all fields.
|
|
|
|
fs.Range(func(n pref.FieldNumber, b pref.RawFields) bool {
|
|
|
|
fs.Set(n, nil)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if got, want := fs.Len(), 0; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(1, raw1)
|
|
|
|
if got, want := fs.Len(), 1; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw1); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(45, raw45)
|
|
|
|
fs.Set(10, raw10) // extension
|
|
|
|
fs.Set(32, raw32)
|
|
|
|
fs.Set(1, nil) // deletion
|
|
|
|
fs.Set(26, raw26)
|
|
|
|
fs.Set(47, raw47) // extension
|
|
|
|
fs.Set(46, raw46) // extension
|
|
|
|
if got, want := fs.Len(), 6; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw32, raw26); !bytes.Equal(got, want) {
|
2018-10-25 14:06:56 -07:00
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
2018-10-31 18:23:42 -07:00
|
|
|
|
|
|
|
// Verify iteration order.
|
|
|
|
i = 0
|
|
|
|
want = []struct {
|
|
|
|
num pref.FieldNumber
|
|
|
|
raw pref.RawFields
|
|
|
|
}{
|
|
|
|
{32, raw32},
|
|
|
|
{26, raw26},
|
|
|
|
{10, raw10}, // extension
|
|
|
|
{45, raw45}, // extension
|
|
|
|
{46, raw46}, // extension
|
|
|
|
{47, raw47}, // extension
|
|
|
|
}
|
|
|
|
fs.Range(func(num pref.FieldNumber, raw pref.RawFields) bool {
|
|
|
|
if i < len(want) {
|
|
|
|
if num != want[i].num || !bytes.Equal(raw, want[i].raw) {
|
|
|
|
t.Errorf("Range(%d) = (%d, %x), want (%d, %x)", i, num, raw, want[i].num, want[i].raw)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Errorf("unexpected Range iteration: %d", i)
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
// Perform partial deletion while iterating.
|
|
|
|
i = 0
|
|
|
|
fs.Range(func(num pref.FieldNumber, raw pref.RawFields) bool {
|
|
|
|
if i%2 == 0 {
|
|
|
|
fs.Set(num, nil)
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
if got, want := fs.Len(), 3; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw26); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Set(15, raw15) // extension
|
|
|
|
fs.Set(3, raw3)
|
|
|
|
fs.Set(99, raw99)
|
|
|
|
if got, want := fs.Len(), 6; got != want {
|
|
|
|
t.Errorf("Len() = %d, want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := m.XXX_unrecognized, joinRaw(raw26, raw3, raw99); !bytes.Equal(got, want) {
|
|
|
|
t.Errorf("data mismatch:\ngot: %x\nwant: %x", got, want)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform partial iteration.
|
|
|
|
i = 0
|
|
|
|
want = []struct {
|
|
|
|
num pref.FieldNumber
|
|
|
|
raw pref.RawFields
|
|
|
|
}{
|
|
|
|
{26, raw26},
|
|
|
|
{3, raw3},
|
|
|
|
}
|
|
|
|
fs.Range(func(num pref.FieldNumber, raw pref.RawFields) bool {
|
|
|
|
if i < len(want) {
|
|
|
|
if num != want[i].num || !bytes.Equal(raw, want[i].raw) {
|
|
|
|
t.Errorf("Range(%d) = (%d, %x), want (%d, %x)", i, num, raw, want[i].num, want[i].raw)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Errorf("unexpected Range iteration: %d", i)
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
return i < 2
|
|
|
|
})
|
2018-10-25 14:06:56 -07:00
|
|
|
}
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
|
2018-11-26 22:32:06 -08:00
|
|
|
func mustMakeExtensionType(x *ptype.StandaloneExtension, v interface{}) pref.ExtensionType {
|
|
|
|
xd, err := ptype.NewExtension(x)
|
|
|
|
if err != nil {
|
|
|
|
panic(xd)
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
return pimpl.Export{}.ExtensionTypeOf(xd, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
parentType = pimpl.Export{}.MessageTypeOf((*legacyTestMessage)(nil))
|
|
|
|
enumV1Type = pimpl.Export{}.EnumTypeOf(proto2_20180125.Message_ChildEnum(0))
|
|
|
|
messageV1Type = pimpl.Export{}.MessageTypeOf((*proto2_20180125.Message_ChildMessage)(nil))
|
|
|
|
enumV2Type = enumProto2Type
|
internal/fileinit: generate reflect data structures from raw descriptors
This CL takes a significantly different approach to generating support
for protobuf reflection. The previous approach involved generating a
large number of Go literals to represent the reflection information.
While that approach was correct, it resulted in too much binary bloat.
The approach taken here initializes the reflection information from
the raw descriptor proto, which is a relatively dense representation
of the protobuf reflection information. In order to keep initialization
cost low, several measures were taken:
* At program init, the bare minimum is parsed in order to initialize
naming information for enums, messages, extensions, and services declared
in the file. This is done because those top-level declarations are often
relevant for registration.
* Only upon first are most of the other data structures for protobuf
reflection actually initialized.
* Instead of using proto.Unmarshal, a hand-written unmarshaler is used.
This allows us to avoid a dependendency on the descriptor proto and also
because the API for the descriptor proto is fundamentally non-performant
since it requires an allocation for every primitive field.
At a high-level, the new implementation lives in internal/fileinit.
Several changes were made to other parts of the repository:
* cmd/protoc-gen-go:
* Stop compressing the raw descriptors. While compression does reduce
the size of the descriptors by approximately 2x, it is a pre-mature
optimization since the descriptors themselves are around 1% of the total
binary bloat that is due to generated protobufs.
* Seeding protobuf reflection from the raw descriptor significantly
simplifies the generator implementation since it is no longer responsible
for constructing a tree of Go literals to represent the same information.
* We remove the generation of the shadow types and instead call
protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation
for every call to ProtoReflect since we need to allocate a tuple that wraps
a pointer to the message value, and a pointer to message type.
* internal/impl:
* We add a MessageType.GoType field and make it required that it is
set prior to first use. This is done so that we can avoid calling
MessageType.init except for when it is actually needed. The allows code
to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the
init code will run, possibly triggering a recursive deadlock (where the
init code depends on getting the Type of some dependency which may be
declared within the same file).
* internal/cmd/generate-types:
* The code to generate reflect/prototype/protofile_list_gen.go was copied
and altered to generated internal/fileinit.desc_list_gen.go.
At a high-level this CL adds significant technical complexity.
However, this is offset by several possible future changes:
* The prototype package can be drastically simplified. We can probably
reimplement internal/legacy to use internal/fileinit instead, allowing us
to drop another dependency on the prototype package. As a result, we can
probably delete most of the constructor types in that package.
* With the prototype package significantly pruned, and the fact that generated
code no longer depend on depends on that package, we can consider merging
what's left of prototype into protodesc.
Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715
Reviewed-on: https://go-review.googlesource.com/c/158539
Reviewed-by: Herbie Ong <herbie@google.com>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
2019-01-18 09:32:24 -08:00
|
|
|
messageV2Type = enumMessagesType.PBType
|
2018-11-26 22:32:06 -08:00
|
|
|
|
|
|
|
extensionTypes = []pref.ExtensionType{
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_bool",
|
|
|
|
Number: 10000,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.BoolKind,
|
|
|
|
Default: pref.ValueOf(true),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_int32",
|
|
|
|
Number: 10001,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.Int32Kind,
|
|
|
|
Default: pref.ValueOf(int32(-12345)),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_uint32",
|
|
|
|
Number: 10002,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.Uint32Kind,
|
|
|
|
Default: pref.ValueOf(uint32(3200)),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_float",
|
|
|
|
Number: 10003,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.FloatKind,
|
|
|
|
Default: pref.ValueOf(float32(3.14159)),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_string",
|
|
|
|
Number: 10004,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.StringKind,
|
|
|
|
Default: pref.ValueOf(string("hello, \"world!\"\n")),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_bytes",
|
|
|
|
Number: 10005,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.BytesKind,
|
|
|
|
Default: pref.ValueOf([]byte("dead\xde\xad\xbe\xefbeef")),
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_enum_v1",
|
|
|
|
Number: 10006,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.EnumKind,
|
|
|
|
Default: pref.ValueOf(pref.EnumNumber(0)),
|
|
|
|
EnumType: enumV1Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, proto2_20180125.Message_ChildEnum(0)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_message_v1",
|
|
|
|
Number: 10007,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.MessageKind,
|
|
|
|
MessageType: messageV1Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, (*proto2_20180125.Message_ChildMessage)(nil)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_enum_v2",
|
|
|
|
Number: 10008,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.EnumKind,
|
|
|
|
Default: pref.ValueOf(pref.EnumNumber(57005)),
|
|
|
|
EnumType: enumV2Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, EnumProto2(0)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.optional_message_v2",
|
|
|
|
Number: 10009,
|
|
|
|
Cardinality: pref.Optional,
|
|
|
|
Kind: pref.MessageKind,
|
|
|
|
MessageType: messageV2Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, (*EnumMessages)(nil)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_bool",
|
|
|
|
Number: 10010,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.BoolKind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_int32",
|
|
|
|
Number: 10011,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.Int32Kind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_uint32",
|
|
|
|
Number: 10012,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.Uint32Kind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_float",
|
|
|
|
Number: 10013,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.FloatKind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_string",
|
|
|
|
Number: 10014,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.StringKind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_bytes",
|
|
|
|
Number: 10015,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.BytesKind,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, nil),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_enum_v1",
|
|
|
|
Number: 10016,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.EnumKind,
|
|
|
|
EnumType: enumV1Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, proto2_20180125.Message_ChildEnum(0)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_message_v1",
|
|
|
|
Number: 10017,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.MessageKind,
|
|
|
|
MessageType: messageV1Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, (*proto2_20180125.Message_ChildMessage)(nil)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_enum_v2",
|
|
|
|
Number: 10018,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.EnumKind,
|
|
|
|
EnumType: enumV2Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, EnumProto2(0)),
|
|
|
|
mustMakeExtensionType(&ptype.StandaloneExtension{
|
|
|
|
FullName: "fizz.buzz.repeated_message_v2",
|
|
|
|
Number: 10019,
|
|
|
|
Cardinality: pref.Repeated,
|
|
|
|
Kind: pref.MessageKind,
|
|
|
|
MessageType: messageV2Type,
|
|
|
|
ExtendedType: parentType,
|
|
|
|
}, (*EnumMessages)(nil)),
|
|
|
|
}
|
|
|
|
|
|
|
|
extensionDescs = []*papi.ExtensionDesc{{
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*bool)(nil),
|
|
|
|
Field: 10000,
|
|
|
|
Name: "fizz.buzz.optional_bool",
|
|
|
|
Tag: "varint,10000,opt,name=optional_bool,def=1",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*int32)(nil),
|
|
|
|
Field: 10001,
|
|
|
|
Name: "fizz.buzz.optional_int32",
|
|
|
|
Tag: "varint,10001,opt,name=optional_int32,def=-12345",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*uint32)(nil),
|
|
|
|
Field: 10002,
|
|
|
|
Name: "fizz.buzz.optional_uint32",
|
|
|
|
Tag: "varint,10002,opt,name=optional_uint32,def=3200",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*float32)(nil),
|
|
|
|
Field: 10003,
|
|
|
|
Name: "fizz.buzz.optional_float",
|
|
|
|
Tag: "fixed32,10003,opt,name=optional_float,def=3.14159",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*string)(nil),
|
|
|
|
Field: 10004,
|
|
|
|
Name: "fizz.buzz.optional_string",
|
|
|
|
Tag: "bytes,10004,opt,name=optional_string,def=hello, \"world!\"\n",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]byte)(nil),
|
|
|
|
Field: 10005,
|
|
|
|
Name: "fizz.buzz.optional_bytes",
|
|
|
|
Tag: "bytes,10005,opt,name=optional_bytes,def=dead\\336\\255\\276\\357beef",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*proto2_20180125.Message_ChildEnum)(nil),
|
|
|
|
Field: 10006,
|
|
|
|
Name: "fizz.buzz.optional_enum_v1",
|
|
|
|
Tag: "varint,10006,opt,name=optional_enum_v1,enum=google.golang.org.proto2_20180125.Message_ChildEnum,def=0",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*proto2_20180125.Message_ChildMessage)(nil),
|
|
|
|
Field: 10007,
|
|
|
|
Name: "fizz.buzz.optional_message_v1",
|
|
|
|
Tag: "bytes,10007,opt,name=optional_message_v1",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*EnumProto2)(nil),
|
|
|
|
Field: 10008,
|
|
|
|
Name: "fizz.buzz.optional_enum_v2",
|
|
|
|
Tag: "varint,10008,opt,name=optional_enum_v2,enum=EnumProto2,def=57005",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: (*EnumMessages)(nil),
|
|
|
|
Field: 10009,
|
|
|
|
Name: "fizz.buzz.optional_message_v2",
|
|
|
|
Tag: "bytes,10009,opt,name=optional_message_v2",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]bool)(nil),
|
|
|
|
Field: 10010,
|
|
|
|
Name: "fizz.buzz.repeated_bool",
|
|
|
|
Tag: "varint,10010,rep,name=repeated_bool",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]int32)(nil),
|
|
|
|
Field: 10011,
|
|
|
|
Name: "fizz.buzz.repeated_int32",
|
|
|
|
Tag: "varint,10011,rep,name=repeated_int32",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]uint32)(nil),
|
|
|
|
Field: 10012,
|
|
|
|
Name: "fizz.buzz.repeated_uint32",
|
|
|
|
Tag: "varint,10012,rep,name=repeated_uint32",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]float32)(nil),
|
|
|
|
Field: 10013,
|
|
|
|
Name: "fizz.buzz.repeated_float",
|
|
|
|
Tag: "fixed32,10013,rep,name=repeated_float",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]string)(nil),
|
|
|
|
Field: 10014,
|
|
|
|
Name: "fizz.buzz.repeated_string",
|
|
|
|
Tag: "bytes,10014,rep,name=repeated_string",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([][]byte)(nil),
|
|
|
|
Field: 10015,
|
|
|
|
Name: "fizz.buzz.repeated_bytes",
|
|
|
|
Tag: "bytes,10015,rep,name=repeated_bytes",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]proto2_20180125.Message_ChildEnum)(nil),
|
|
|
|
Field: 10016,
|
|
|
|
Name: "fizz.buzz.repeated_enum_v1",
|
|
|
|
Tag: "varint,10016,rep,name=repeated_enum_v1,enum=google.golang.org.proto2_20180125.Message_ChildEnum",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]*proto2_20180125.Message_ChildMessage)(nil),
|
|
|
|
Field: 10017,
|
|
|
|
Name: "fizz.buzz.repeated_message_v1",
|
|
|
|
Tag: "bytes,10017,rep,name=repeated_message_v1",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]EnumProto2)(nil),
|
|
|
|
Field: 10018,
|
|
|
|
Name: "fizz.buzz.repeated_enum_v2",
|
|
|
|
Tag: "varint,10018,rep,name=repeated_enum_v2,enum=EnumProto2",
|
|
|
|
}, {
|
|
|
|
ExtendedType: (*legacyTestMessage)(nil),
|
|
|
|
ExtensionType: ([]*EnumMessages)(nil),
|
|
|
|
Field: 10019,
|
|
|
|
Name: "fizz.buzz.repeated_message_v2",
|
|
|
|
Tag: "bytes,10019,rep,name=repeated_message_v2",
|
|
|
|
}}
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestLegacyExtensions(t *testing.T) {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
opts := cmp.Options{cmp.Comparer(func(x, y *proto2_20180125.Message_ChildMessage) bool {
|
|
|
|
return x == y // pointer compare messages for object identity
|
|
|
|
})}
|
|
|
|
|
|
|
|
m := new(legacyTestMessage)
|
2018-11-26 22:32:06 -08:00
|
|
|
fs := pimpl.Export{}.MessageOf(m).KnownFields()
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
ts := fs.ExtensionTypes()
|
|
|
|
|
|
|
|
if n := fs.Len(); n != 0 {
|
|
|
|
t.Errorf("KnownFields.Len() = %v, want 0", n)
|
|
|
|
}
|
|
|
|
if n := ts.Len(); n != 0 {
|
|
|
|
t.Errorf("ExtensionFieldTypes.Len() = %v, want 0", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register all the extension types.
|
2018-11-26 22:32:06 -08:00
|
|
|
for _, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
ts.Register(xt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that getting the zero value returns the default value for scalars,
|
all: rename Vector as List
The terminology Vector does not occur in protobuf documentation at all,
so we should rename the Go use of the term to something more recognizable.
As such, all instances that match the regexp "[Vv]ect(or)?" were replaced.
The C++ documentation uses the term "Repeated", which is a reasonable name.
However, the term became overloaded in 2014, when maps were added as a feature
and implementated under the hood as repeated fields. This is confusing as it
means "repeated" could either refer to repeated fields proper (i.e., explicitly
marked with the "repeated" label in the proto file) or map fields. In the case
of the C++ reflective API, this is not a problem since repeated fields proper
and map fields are interacted with through the same RepeatedField type.
In Go, we do not use a single type to handle both types of repeated fields:
1) We are coming up with the Go protobuf reflection API for the first time
and so do not need to piggy-back on the repeated fields API to remain backwards
compatible since no former usages of Go protobuf reflection exists.
2) Map fields are commonly represented in Go as the Go map type, which do not
preserve ordering information. As such it is fundamentally impossible to present
an unordered map as a consistently ordered list. Thus, Go needs two different
interfaces for lists and maps.
Given the above situation, "Repeated" is not a great term to use since it
refers to two different things (when we only want one of the meanings).
To distinguish between the two, we'll use the terms "List" and "Map" instead.
There is some precedence for the term "List" in the protobuf codebase
(e.g., "getRepeatedInt32List").
Change-Id: Iddcdb6b78e1e60c14fa4ca213c15f45e214b967b
Reviewed-on: https://go-review.googlesource.com/c/149657
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-14 14:05:19 -08:00
|
|
|
// nil for singular messages, and an empty list for repeated fields.
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
defaultValues := []interface{}{
|
|
|
|
bool(true),
|
|
|
|
int32(-12345),
|
|
|
|
uint32(3200),
|
|
|
|
float32(3.14159),
|
|
|
|
string("hello, \"world!\"\n"),
|
|
|
|
[]byte("dead\xde\xad\xbe\xefbeef"),
|
|
|
|
proto2_20180125.Message_ALPHA,
|
|
|
|
nil,
|
2018-11-14 21:59:49 -08:00
|
|
|
EnumProto2(0xdead),
|
|
|
|
nil,
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
new([]bool),
|
|
|
|
new([]int32),
|
|
|
|
new([]uint32),
|
|
|
|
new([]float32),
|
|
|
|
new([]string),
|
|
|
|
new([][]byte),
|
|
|
|
new([]proto2_20180125.Message_ChildEnum),
|
|
|
|
new([]*proto2_20180125.Message_ChildMessage),
|
2018-11-14 21:59:49 -08:00
|
|
|
new([]EnumProto2),
|
|
|
|
new([]*EnumMessages),
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
for i, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
var got interface{}
|
reflect/protoreflect: clarify Get semantics on unpopulated fields
Clearly specify that Get on an unpopulated field:
* returns the default value for scalars
* returns a mutable (but empty) List for repeated fields
* returns a mutable (but empty) Map for map fields
* returns an invalid value for message fields
The difference in semantics between List+Maps and Messages is because
protobuf semantics provide no distinction between an unpopulated and empty list
or map. On the other hand, there is a semantic difference between an unpopulated
message and an empty message.
Default values for scalars is trivial to implement with FieldDescriptor.Default.
A mutable, but empty List and Map is easy to implement for known fields since
known fields are generated as a slice or map field in a struct.
Since struct fields are addressable, the implementation can just return a
reference to the slice or map.
Repeated, extension fields are a little more tricky since extension fields
are implemented under the hood as a map[FieldNumber]Extension.
Rather than allocating an empty list in KnownFields.Get upon first retrieval
(which presents a race), delegate the work to ExtensionFieldTypes.Register,
which must occur before any Get operation. Register is not a concurrent-safe
operation, so that is an excellent time to initilize empty lists.
The implementation of extensions will need to be careful that Clear on a repeated
field simply truncates it zero instead of deleting the object.
For unpopulated messages, we return an invalid value, instead of the prior
behavior of returning a typed nil-pointer to the Go type for the message.
The approach is problematic because it assumes that
1) all messages are always implemented on a pointer reciever
2) a typed nil-pointer is an appropriate "read-only, but empty" message
These assumptions are not true of all message types (e.g., dynamic messages).
Change-Id: Ie96e6744c890308d9de738b6cf01d3b19e7e7c6a
Reviewed-on: https://go-review.googlesource.com/c/150319
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-19 14:26:06 -08:00
|
|
|
if v := fs.Get(xt.Number()); v.IsValid() {
|
|
|
|
got = xt.InterfaceOf(v)
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
|
|
|
want := defaultValues[i]
|
|
|
|
if diff := cmp.Diff(want, got, opts); diff != "" {
|
|
|
|
t.Errorf("KnownFields.Get(%d) mismatch (-want +got):\n%v", xt.Number(), diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All fields should be unpopulated.
|
2018-11-26 22:32:06 -08:00
|
|
|
for _, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
if fs.Has(xt.Number()) {
|
|
|
|
t.Errorf("KnownFields.Has(%d) = true, want false", xt.Number())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
all: rename Vector as List
The terminology Vector does not occur in protobuf documentation at all,
so we should rename the Go use of the term to something more recognizable.
As such, all instances that match the regexp "[Vv]ect(or)?" were replaced.
The C++ documentation uses the term "Repeated", which is a reasonable name.
However, the term became overloaded in 2014, when maps were added as a feature
and implementated under the hood as repeated fields. This is confusing as it
means "repeated" could either refer to repeated fields proper (i.e., explicitly
marked with the "repeated" label in the proto file) or map fields. In the case
of the C++ reflective API, this is not a problem since repeated fields proper
and map fields are interacted with through the same RepeatedField type.
In Go, we do not use a single type to handle both types of repeated fields:
1) We are coming up with the Go protobuf reflection API for the first time
and so do not need to piggy-back on the repeated fields API to remain backwards
compatible since no former usages of Go protobuf reflection exists.
2) Map fields are commonly represented in Go as the Go map type, which do not
preserve ordering information. As such it is fundamentally impossible to present
an unordered map as a consistently ordered list. Thus, Go needs two different
interfaces for lists and maps.
Given the above situation, "Repeated" is not a great term to use since it
refers to two different things (when we only want one of the meanings).
To distinguish between the two, we'll use the terms "List" and "Map" instead.
There is some precedence for the term "List" in the protobuf codebase
(e.g., "getRepeatedInt32List").
Change-Id: Iddcdb6b78e1e60c14fa4ca213c15f45e214b967b
Reviewed-on: https://go-review.googlesource.com/c/149657
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-14 14:05:19 -08:00
|
|
|
// Set some values and append to values to the lists.
|
2018-11-27 18:45:07 -08:00
|
|
|
m1a := &proto2_20180125.Message_ChildMessage{F1: scalar.String("m1a")}
|
|
|
|
m1b := &proto2_20180125.Message_ChildMessage{F1: scalar.String("m2b")}
|
2018-11-14 21:59:49 -08:00
|
|
|
m2a := &EnumMessages{EnumP2: EnumProto2(0x1b).Enum()}
|
|
|
|
m2b := &EnumMessages{EnumP2: EnumProto2(0x2b).Enum()}
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
setValues := []interface{}{
|
|
|
|
bool(false),
|
|
|
|
int32(-54321),
|
|
|
|
uint32(6400),
|
|
|
|
float32(2.71828),
|
|
|
|
string("goodbye, \"world!\"\n"),
|
|
|
|
[]byte("live\xde\xad\xbe\xefchicken"),
|
|
|
|
proto2_20180125.Message_CHARLIE,
|
2018-11-14 21:59:49 -08:00
|
|
|
m1a,
|
|
|
|
EnumProto2(0xbeef),
|
|
|
|
m2a,
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
&[]bool{true},
|
|
|
|
&[]int32{-1000},
|
|
|
|
&[]uint32{1280},
|
|
|
|
&[]float32{1.6180},
|
|
|
|
&[]string{"zero"},
|
|
|
|
&[][]byte{[]byte("zero")},
|
|
|
|
&[]proto2_20180125.Message_ChildEnum{proto2_20180125.Message_BRAVO},
|
2018-11-14 21:59:49 -08:00
|
|
|
&[]*proto2_20180125.Message_ChildMessage{m1b},
|
|
|
|
&[]EnumProto2{0xdead},
|
|
|
|
&[]*EnumMessages{m2b},
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
for i, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
fs.Set(xt.Number(), xt.ValueOf(setValues[i]))
|
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
for i, xt := range extensionTypes[len(extensionTypes)/2:] {
|
|
|
|
v := extensionTypes[i].ValueOf(setValues[i])
|
all: rename Vector as List
The terminology Vector does not occur in protobuf documentation at all,
so we should rename the Go use of the term to something more recognizable.
As such, all instances that match the regexp "[Vv]ect(or)?" were replaced.
The C++ documentation uses the term "Repeated", which is a reasonable name.
However, the term became overloaded in 2014, when maps were added as a feature
and implementated under the hood as repeated fields. This is confusing as it
means "repeated" could either refer to repeated fields proper (i.e., explicitly
marked with the "repeated" label in the proto file) or map fields. In the case
of the C++ reflective API, this is not a problem since repeated fields proper
and map fields are interacted with through the same RepeatedField type.
In Go, we do not use a single type to handle both types of repeated fields:
1) We are coming up with the Go protobuf reflection API for the first time
and so do not need to piggy-back on the repeated fields API to remain backwards
compatible since no former usages of Go protobuf reflection exists.
2) Map fields are commonly represented in Go as the Go map type, which do not
preserve ordering information. As such it is fundamentally impossible to present
an unordered map as a consistently ordered list. Thus, Go needs two different
interfaces for lists and maps.
Given the above situation, "Repeated" is not a great term to use since it
refers to two different things (when we only want one of the meanings).
To distinguish between the two, we'll use the terms "List" and "Map" instead.
There is some precedence for the term "List" in the protobuf codebase
(e.g., "getRepeatedInt32List").
Change-Id: Iddcdb6b78e1e60c14fa4ca213c15f45e214b967b
Reviewed-on: https://go-review.googlesource.com/c/149657
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-14 14:05:19 -08:00
|
|
|
fs.Get(xt.Number()).List().Append(v)
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the values and check for equality.
|
|
|
|
getValues := []interface{}{
|
|
|
|
bool(false),
|
|
|
|
int32(-54321),
|
|
|
|
uint32(6400),
|
|
|
|
float32(2.71828),
|
|
|
|
string("goodbye, \"world!\"\n"),
|
|
|
|
[]byte("live\xde\xad\xbe\xefchicken"),
|
|
|
|
proto2_20180125.Message_ChildEnum(proto2_20180125.Message_CHARLIE),
|
2018-11-14 21:59:49 -08:00
|
|
|
m1a,
|
|
|
|
EnumProto2(0xbeef),
|
|
|
|
m2a,
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
&[]bool{true, false},
|
|
|
|
&[]int32{-1000, -54321},
|
|
|
|
&[]uint32{1280, 6400},
|
|
|
|
&[]float32{1.6180, 2.71828},
|
|
|
|
&[]string{"zero", "goodbye, \"world!\"\n"},
|
|
|
|
&[][]byte{[]byte("zero"), []byte("live\xde\xad\xbe\xefchicken")},
|
|
|
|
&[]proto2_20180125.Message_ChildEnum{proto2_20180125.Message_BRAVO, proto2_20180125.Message_CHARLIE},
|
2018-11-14 21:59:49 -08:00
|
|
|
&[]*proto2_20180125.Message_ChildMessage{m1b, m1a},
|
|
|
|
&[]EnumProto2{0xdead, 0xbeef},
|
|
|
|
&[]*EnumMessages{m2b, m2a},
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
for i, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
got := xt.InterfaceOf(fs.Get(xt.Number()))
|
|
|
|
want := getValues[i]
|
|
|
|
if diff := cmp.Diff(want, got, opts); diff != "" {
|
|
|
|
t.Errorf("KnownFields.Get(%d) mismatch (-want +got):\n%v", xt.Number(), diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-14 21:59:49 -08:00
|
|
|
if n := fs.Len(); n != 20 {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
t.Errorf("KnownFields.Len() = %v, want 0", n)
|
|
|
|
}
|
2018-11-14 21:59:49 -08:00
|
|
|
if n := ts.Len(); n != 20 {
|
|
|
|
t.Errorf("ExtensionFieldTypes.Len() = %v, want 20", n)
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the field for all extension types.
|
2018-11-26 22:32:06 -08:00
|
|
|
for _, xt := range extensionTypes[:len(extensionTypes)/2] {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
fs.Clear(xt.Number())
|
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
for i, xt := range extensionTypes[len(extensionTypes)/2:] {
|
reflect/protoreflect: clarify Get semantics on unpopulated fields
Clearly specify that Get on an unpopulated field:
* returns the default value for scalars
* returns a mutable (but empty) List for repeated fields
* returns a mutable (but empty) Map for map fields
* returns an invalid value for message fields
The difference in semantics between List+Maps and Messages is because
protobuf semantics provide no distinction between an unpopulated and empty list
or map. On the other hand, there is a semantic difference between an unpopulated
message and an empty message.
Default values for scalars is trivial to implement with FieldDescriptor.Default.
A mutable, but empty List and Map is easy to implement for known fields since
known fields are generated as a slice or map field in a struct.
Since struct fields are addressable, the implementation can just return a
reference to the slice or map.
Repeated, extension fields are a little more tricky since extension fields
are implemented under the hood as a map[FieldNumber]Extension.
Rather than allocating an empty list in KnownFields.Get upon first retrieval
(which presents a race), delegate the work to ExtensionFieldTypes.Register,
which must occur before any Get operation. Register is not a concurrent-safe
operation, so that is an excellent time to initilize empty lists.
The implementation of extensions will need to be careful that Clear on a repeated
field simply truncates it zero instead of deleting the object.
For unpopulated messages, we return an invalid value, instead of the prior
behavior of returning a typed nil-pointer to the Go type for the message.
The approach is problematic because it assumes that
1) all messages are always implemented on a pointer reciever
2) a typed nil-pointer is an appropriate "read-only, but empty" message
These assumptions are not true of all message types (e.g., dynamic messages).
Change-Id: Ie96e6744c890308d9de738b6cf01d3b19e7e7c6a
Reviewed-on: https://go-review.googlesource.com/c/150319
Reviewed-by: Damien Neil <dneil@google.com>
2018-11-19 14:26:06 -08:00
|
|
|
if i%2 == 0 {
|
|
|
|
fs.Clear(xt.Number())
|
|
|
|
} else {
|
|
|
|
fs.Get(xt.Number()).List().Truncate(0)
|
|
|
|
}
|
|
|
|
}
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
if n := fs.Len(); n != 0 {
|
|
|
|
t.Errorf("KnownFields.Len() = %v, want 0", n)
|
|
|
|
}
|
2018-11-14 21:59:49 -08:00
|
|
|
if n := ts.Len(); n != 20 {
|
|
|
|
t.Errorf("ExtensionFieldTypes.Len() = %v, want 20", n)
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// De-register all extension types.
|
2018-11-26 22:32:06 -08:00
|
|
|
for _, xt := range extensionTypes {
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
ts.Remove(xt)
|
|
|
|
}
|
|
|
|
if n := fs.Len(); n != 0 {
|
|
|
|
t.Errorf("KnownFields.Len() = %v, want 0", n)
|
|
|
|
}
|
|
|
|
if n := ts.Len(); n != 0 {
|
|
|
|
t.Errorf("ExtensionFieldTypes.Len() = %v, want 0", n)
|
|
|
|
}
|
2018-11-26 22:32:06 -08:00
|
|
|
}
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
|
2018-11-26 22:32:06 -08:00
|
|
|
func TestExtensionConvert(t *testing.T) {
|
|
|
|
for i := range extensionTypes {
|
|
|
|
i := i
|
|
|
|
t.Run("", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
wantType := extensionTypes[i]
|
|
|
|
wantDesc := extensionDescs[i]
|
|
|
|
gotType := plegacy.Export{}.ExtensionTypeFromDesc(wantDesc)
|
|
|
|
gotDesc := plegacy.Export{}.ExtensionDescFromType(wantType)
|
|
|
|
|
|
|
|
// TODO: We need a test package to compare descriptors.
|
|
|
|
type list interface {
|
|
|
|
Len() int
|
|
|
|
pragma.DoNotImplement
|
|
|
|
}
|
|
|
|
opts := cmp.Options{
|
|
|
|
cmp.Comparer(func(x, y reflect.Type) bool {
|
|
|
|
return x == y
|
|
|
|
}),
|
|
|
|
cmp.Transformer("", func(x list) []interface{} {
|
|
|
|
out := make([]interface{}, x.Len())
|
|
|
|
v := reflect.ValueOf(x)
|
|
|
|
for i := 0; i < x.Len(); i++ {
|
|
|
|
m := v.MethodByName("Get")
|
|
|
|
out[i] = m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}),
|
|
|
|
cmp.Transformer("", func(x pref.Descriptor) map[string]interface{} {
|
|
|
|
out := make(map[string]interface{})
|
|
|
|
v := reflect.ValueOf(x)
|
|
|
|
for i := 0; i < v.NumMethod(); i++ {
|
|
|
|
name := v.Type().Method(i).Name
|
|
|
|
if m := v.Method(i); m.Type().NumIn() == 0 && m.Type().NumOut() == 1 {
|
|
|
|
switch name {
|
|
|
|
case "New":
|
|
|
|
// Ignore New since it a constructor.
|
|
|
|
case "Options":
|
|
|
|
// Ignore descriptor options since protos are not cmperable.
|
|
|
|
case "EnumType", "MessageType", "ExtendedType":
|
|
|
|
// Avoid descending into a dependency to avoid a cycle.
|
|
|
|
// Just record the full name if available.
|
|
|
|
//
|
|
|
|
// TODO: Cycle support in cmp would be useful here.
|
|
|
|
v := m.Call(nil)[0]
|
|
|
|
if !v.IsNil() {
|
|
|
|
out[name] = v.Interface().(pref.Descriptor).FullName()
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
out[name] = m.Call(nil)[0].Interface()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}),
|
|
|
|
cmp.Transformer("", func(v pref.Value) interface{} {
|
|
|
|
return v.Interface()
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(&wantType, &gotType, opts); diff != "" {
|
|
|
|
t.Errorf("ExtensionType mismatch (-want, +got):\n%v", diff)
|
|
|
|
}
|
|
|
|
|
|
|
|
opts = cmp.Options{
|
|
|
|
cmpopts.IgnoreFields(papi.ExtensionDesc{}, "Type"),
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(wantDesc, gotDesc, opts); diff != "" {
|
|
|
|
t.Errorf("ExtensionDesc mismatch (-want, +got):\n%v", diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
internal/impl: support legacy extension fields
Implement support for extension fields for messages that use the v1
data structures for extensions. The legacyExtensionFields type wraps a
v1 map to implement the v2 protoreflect.KnownFields interface.
Working on this change revealed a bug in the dynamic construction of
message types for protobuf messages that had cyclic dependencies (e.g.,
message Foo has a sub-field of message Bar, and Bar has a sub-field of Foo).
In such a situation, a deadlock occurs because initialization code depends on
the very initialization code that is currently running. To break these cycles,
we make some systematic changes listed in the following paragraphs.
Generally speaking, we separate the logic for construction and wrapping,
where constuction does not recursively rely on dependencies,
while wrapping may recursively inspect dependencies.
Promote the MessageType.MessageOf method as a standalone MessageOf function
that dynamically finds the proper *MessageType to use. We make it such that
MessageType only supports two forms of messages types:
* Those that fully implement the v2 API.
* Those that do not implement the v2 API at all.
This removes support for the hybrid form that was exploited by message_test.go
In impl/message_test.go, switch each message to look more like how future
generated messages will look like. This is done in reaction to the fact that
MessageType.MessageOf no longer exists.
In value/{map,vector}.go, fix Unwrap to return a pointer since the underlying
reflect.Value is addressable reference value, not a pointer value.
In value/convert.go, split the logic apart so that obtaining a v2 type and
wrapping a type as v2 are distinct operations. Wrapping requires further
initialization than simply creating the initial message type, and calling it
during initial construction would lead to a deadlock.
In protoreflect/go_type.go, we switch back to a lazy initialization of GoType
to avoid a deadlock since the user-provided fn may rely on the fact that
prototype.GoMessage returned.
Change-Id: I5dea00e36fe1a9899bd2ac0aed2c8e51d5d87420
Reviewed-on: https://go-review.googlesource.com/c/148826
Reviewed-by: Herbie Ong <herbie@google.com>
2018-11-06 13:05:20 -08:00
|
|
|
}
|