Skip to content

jsonpb: don't allocate memory for null-value reference types #404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 8, 2017
Merged

jsonpb: don't allocate memory for null-value reference types #404

merged 2 commits into from
Aug 8, 2017

Conversation

tamird
Copy link
Contributor

@tamird tamird commented Aug 7, 2017

This was missed in 748d386.

@cybrcodr another small issue following on from discussion in #338.

Copy link
Contributor

@cybrcodr cybrcodr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sending in a fix.

Please also add tests for both map and slice. Sadly current unmarshal test approach is flawed and won't expose the bug that this PR fixes. I filed issue #405 for that and we can have a separate PR to fix the tests.

jsonpb/jsonpb.go Outdated
// Allocate memory for pointer fields.
if targetType.Kind() == reflect.Ptr {
switch targetType.Kind() {
case reflect.Map, reflect.Ptr, reflect.Slice:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not combine the logic for map and slice here. It's better to just leave this for pointer type alone.

Suggest adding the following logic instead ...

if string(inputValue) == "null" {
    return nil
}

after original file lines 917 for handling slice

if targetType.Kind() == reflect.Slice...

and after line 933

if targetType.Kind() == reflect.Map {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and added tests. Thanks for making me do that; this implementation turned out not to be correct.

jsonpb/jsonpb.go Outdated
for i := 0; i < len; i++ {
if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil {
return err
if l := len(slc); l > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if user explicitly specifies an empty JSON list/map then the deserialized Go struct field should also be an empty slice/map. If JSON field is not specified or if explicitly set to "null", then leave Go field as nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

reflect.TypeOf(pb.Maps{}),
} {
in := reflect.New(ty).Interface().(proto.Message)
if err := (&Marshaler{EmitDefaults: true}).Marshal(&b, in); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to test unmarshaler by itself and not rely on the Marshaler at all. Basically, provide JSON strings to unmarshal and compare with expected result.

I also just realized that proto.Equal will not properly compare nil vs empty on these, so I reflect.DeepEqual is ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@cybrcodr cybrcodr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Just a minor nit.

if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil {
return err
}
if mp != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional. Do early return for mp == nil, that way there's no need to indent block below.

if mp == nil {
    return nil
}
target.Set(reflect.MakeMap(targetType))
...

Same above if you're going to make this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather leave it, avoiding the repetition of return nil. 6 of one, half-dozen of the other.

@cybrcodr cybrcodr merged commit 1909bc2 into golang:master Aug 8, 2017
@tamird tamird deleted the dont-allocate-slices-maps branch August 8, 2017 02:22
@golang golang locked and limited conversation to collaborators Jun 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants