Skip to content

Commit

Permalink
Fix mapper panic on map[string]*string handling
Browse files Browse the repository at this point in the history
  • Loading branch information
t0yv0 authored and aq17 committed Dec 6, 2022
1 parent bb1ef96 commit 751d795
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 8 deletions.
19 changes: 11 additions & 8 deletions sdk/go/common/util/mapper/mapper_decode.go
Expand Up @@ -151,16 +151,19 @@ func (md *mapper) adjustValueForAssignment(val reflect.Value,
// A simple conversion exists to make this right.
val = val.Convert(to)
} else if to.Kind() == reflect.Ptr && val.Type().AssignableTo(to.Elem()) {
// If the target is a pointer, turn the target into a pointer. If it's not addressable, make a copy.
if val.CanAddr() {
val = val.Addr()
// Here the destination type (to) is a pointer to a type that accepts val.
var adjusted reflect.Value // var adjusted *toElem
if val.CanAddr() && val.Addr().Type().AssignableTo(to) {
// If taking the address of val makes this right, do it.
adjusted = val.Addr() // adjusted = &val
} else {
slot := reflect.New(val.Type().Elem())
copy := reflect.ValueOf(val.Interface())
contract.Assert(copy.CanAddr())
slot.Set(copy)
val = slot
// Otherwise create a fresh pointer of the desired type and point it to val.
adjusted = reflect.New(to.Elem()) // adjusted = new(toElem)
adjusted.Elem().Set(val) // *adjusted = val
}
// In either case, the loop condition should be sastisfied at this point.
contract.Assert(adjusted.Type().AssignableTo(to))
return adjusted, nil
} else if val.Kind() == reflect.Interface {
// It could be that the source is an interface{} with the right element type (or the right element type
// through a series of successive conversions); go ahead and give it a try.
Expand Down
41 changes: 41 additions & 0 deletions sdk/go/common/util/mapper/mapper_test.go
Expand Up @@ -573,3 +573,44 @@ func TestBasicUnmap(t *testing.T) {
assert.False(t, hash1h)
}
}

func TestReproduceMapStringPointerTurnaroundIssue(t *testing.T) {
t.Parallel()

type X struct {
Args map[string]*string `pulumi:"args,optional"`
}

xToMap := func(build X) (map[string]interface{}, error) {
m, err := New(nil).Encode(build)
if err != nil {
return nil, err
}
return m, nil
}

xFromMap := func(pm map[string]interface{}) (X, error) {
var build X
err := New(nil).Decode(pm, &build)
if err != nil {
return X{}, err
}
return build, nil
}

value := "value"
expected := X{
Args: map[string]*string{
"key": &value,
},
}

encodedMap, err := xToMap(expected)
require.NoError(t, err)
t.Logf("encodedMap: %v", encodedMap)

back, err2 := xFromMap(encodedMap)
require.NoError(t, err2)

require.Equal(t, expected, back)
}

0 comments on commit 751d795

Please sign in to comment.