[fidl][go] Unmarshaling of xunion bytes to union structures

When there is the correct signal in the header (TBD), unions
will be unmarshaled from xunion bytes as part of the xunion
to union migration.

Change-Id: If1505ac87dae09e5660c114ccfaadf4d89919b18
diff --git a/src/syscall/zx/fidl/bindingstest/impl.go b/src/syscall/zx/fidl/bindingstest/impl.go
index 16511cb..814f580 100644
--- a/src/syscall/zx/fidl/bindingstest/impl.go
+++ b/src/syscall/zx/fidl/bindingstest/impl.go
@@ -311,6 +311,17 @@
 	return _mXUnion1Struct
 }
 
+type XUnion1AsUnionStruct struct {
+	_    struct{}       `fidl:"s,16,8" fidl2:"s,16,8"`
+	Xuau XUnion1AsUnion `fidl:"0"`
+}
+
+var _mXUnion1AsUnionStruct = _bindings.CreateLazyMarshaler(XUnion1AsUnionStruct{})
+
+func (msg *XUnion1AsUnionStruct) Marshaler() _bindings.Marshaler {
+	return _mXUnion1AsUnionStruct
+}
+
 type TestXUnion1 struct {
 	_ struct{} `fidl:"s,48,8" fidl2:"s,48,8"`
 	A XUnion1  `fidl:"0"`
@@ -527,10 +538,10 @@
 
 type Union1 struct {
 	I_union1Tag `fidl:"u,16,8" fidl2:"u,16,8"`
-	A           [3]int8
-	B           TestSimple
-	C           *TestSimple
-	D           float32
+	A           [3]int8     `fidl:"1140208200" fidl2:"1140208200"`
+	B           TestSimple  `fidl:"1101191659" fidl2:"1101191659"`
+	C           *TestSimple `fidl:"310804024" fidl2:"310804024"`
+	D           float32     `fidl:"1193101976" fidl2:"1193101976"`
 }
 
 func (u *Union1) Which() I_union1Tag {
@@ -581,6 +592,59 @@
 	return _u
 }
 
+type I_xUnion1AsUnionTag uint32
+
+const (
+	_ I_xUnion1AsUnionTag = iota
+	XUnion1AsUnionA
+	XUnion1AsUnionB
+	XUnion1AsUnionD
+)
+
+type XUnion1AsUnion struct {
+	I_xUnion1AsUnionTag `fidl:"u,16,8" fidl2:"u,16,8"`
+	A                   [3]int8    `fidl:"1815116267" fidl2:"1815116267"`
+	B                   TestSimple `fidl:"219568201" fidl2:"219568201"`
+	D                   float32    `fidl:"1047172132" fidl2:"1047172132"`
+}
+
+func (u *XUnion1AsUnion) Which() I_xUnion1AsUnionTag {
+	return u.I_xUnion1AsUnionTag
+}
+
+func (u *XUnion1AsUnion) SetA(a [3]int8) {
+	u.I_xUnion1AsUnionTag = XUnion1AsUnionA
+	u.A = a
+}
+
+func XUnion1AsUnionWithA(a [3]int8) XUnion1AsUnion {
+	var _u XUnion1AsUnion
+	_u.SetA(a)
+	return _u
+}
+
+func (u *XUnion1AsUnion) SetB(b TestSimple) {
+	u.I_xUnion1AsUnionTag = XUnion1AsUnionB
+	u.B = b
+}
+
+func XUnion1AsUnionWithB(b TestSimple) XUnion1AsUnion {
+	var _u XUnion1AsUnion
+	_u.SetB(b)
+	return _u
+}
+
+func (u *XUnion1AsUnion) SetD(d float32) {
+	u.I_xUnion1AsUnionTag = XUnion1AsUnionD
+	u.D = d
+}
+
+func XUnion1AsUnionWithD(d float32) XUnion1AsUnion {
+	var _u XUnion1AsUnion
+	_u.SetD(d)
+	return _u
+}
+
 type I_simpleUnionTag uint32
 
 const (
@@ -594,11 +658,11 @@
 
 type SimpleUnion struct {
 	I_simpleUnionTag `fidl:"u,24,8" fidl2:"u,24,8"`
-	I32              int32
-	I64              int64
-	S                Int64Struct
-	Os               *Int64Struct
-	Str              string
+	I32              int32        `fidl:"2133387115" fidl2:"2133387115"`
+	I64              int64        `fidl:"809723265" fidl2:"809723265"`
+	S                Int64Struct  `fidl:"1133668882" fidl2:"1133668882"`
+	Os               *Int64Struct `fidl:"1823223580" fidl2:"1823223580"`
+	Str              string       `fidl:"1886911784," fidl2:"1886911784,"`
 }
 
 func (u *SimpleUnion) Which() I_simpleUnionTag {
@@ -670,8 +734,8 @@
 
 type IpAddressConfig struct {
 	I_ipAddressConfigTag `fidl:"u,28,4" fidl2:"u,28,4"`
-	PaddingSize24Align4  [6]uint32
-	Dhcp                 bool
+	PaddingSize24Align4  [6]uint32 `fidl:"1850696643" fidl2:"1850696643"`
+	Dhcp                 bool      `fidl:"500027731" fidl2:"500027731"`
 }
 
 func (u *IpAddressConfig) Which() I_ipAddressConfigTag {
diff --git a/src/syscall/zx/fidl/bindingstest/test.test.fidl b/src/syscall/zx/fidl/bindingstest/test.test.fidl
index 9583b60..1ade4a7 100644
--- a/src/syscall/zx/fidl/bindingstest/test.test.fidl
+++ b/src/syscall/zx/fidl/bindingstest/test.test.fidl
@@ -143,6 +143,16 @@
     XUnion1 xu;
 };
 
+union XUnion1AsUnion {
+    array<int8>:3 a;
+    TestSimple b;
+    float32 d;
+};
+
+struct XUnion1AsUnionStruct {
+    XUnion1AsUnion xuau;
+};
+
 struct TestXUnion1 {
     XUnion1 a;
     XUnion1? b;
@@ -269,7 +279,8 @@
     uint64 offset;
 };
 
-protocol EthernetDevice {};
+protocol EthernetDevice {
+};
 
 struct InterfaceConfig {
     string name;
diff --git a/src/syscall/zx/fidl/conformance/impl.go b/src/syscall/zx/fidl/conformance/impl.go
index 28060dc..3b9126d 100644
--- a/src/syscall/zx/fidl/conformance/impl.go
+++ b/src/syscall/zx/fidl/conformance/impl.go
@@ -544,8 +544,8 @@
 
 type IpAddressConfig struct {
 	I_ipAddressConfigTag `fidl:"u,28,4" fidl2:"u,28,4"`
-	PaddingSize24Align4  [6]uint32
-	Dhcp                 bool
+	PaddingSize24Align4  [6]uint32 `fidl:"1136452149" fidl2:"1136452149"`
+	Dhcp                 bool      `fidl:"1619238975" fidl2:"1619238975"`
 }
 
 func (u *IpAddressConfig) Which() I_ipAddressConfigTag {
@@ -584,8 +584,8 @@
 
 type UnionWithEmptyStruct struct {
 	I_unionWithEmptyStructTag `fidl:"u,16,8" fidl2:"u,16,8"`
-	S                         EmptyStruct
-	S2                        *EmptyStruct
+	S                         EmptyStruct  `fidl:"2012545430" fidl2:"2012545430"`
+	S2                        *EmptyStruct `fidl:"1404939714" fidl2:"1404939714"`
 }
 
 func (u *UnionWithEmptyStruct) Which() I_unionWithEmptyStructTag {
@@ -627,11 +627,11 @@
 
 type SimpleUnion struct {
 	I_simpleUnionTag `fidl:"u,24,8" fidl2:"u,24,8"`
-	I32              int32
-	I64              int64
-	S                Int64Struct
-	Os               *Int64Struct
-	Str              string
+	I32              int32        `fidl:"1667828146" fidl2:"1667828146"`
+	I64              int64        `fidl:"1110539033" fidl2:"1110539033"`
+	S                Int64Struct  `fidl:"684890561" fidl2:"684890561"`
+	Os               *Int64Struct `fidl:"1501363482" fidl2:"1501363482"`
+	Str              string       `fidl:"498325473," fidl2:"498325473,"`
 }
 
 func (u *SimpleUnion) Which() I_simpleUnionTag {
diff --git a/src/syscall/zx/fidl/debug_util.go b/src/syscall/zx/fidl/debug_util.go
index ad9c562..b95247e 100644
--- a/src/syscall/zx/fidl/debug_util.go
+++ b/src/syscall/zx/fidl/debug_util.go
@@ -72,8 +72,18 @@
 		fallthrough
 	case reflect.Struct:
 		return rt.Name()
+	case reflect.Complex64:
+		return "[complex64]"
+	case reflect.Complex128:
+		return "[complex128]"
+	case reflect.Chan:
+		return "[chan]"
+	case reflect.Func:
+		return "[func]"
+	case reflect.UnsafePointer:
+		return "[unsafeptr]"
 	default:
-		panic("unsupported type")
+		panic("unsupported type kind " + strconv.Itoa(int(rt.Kind())))
 	}
 }
 
@@ -138,7 +148,17 @@
 			itemStrs = append(itemStrs, rv.Type().Field(i).Name+": "+buildValueString(rv.Field(i)))
 		}
 		return buildTypeString(rv.Type()) + "{" + strings.Join(itemStrs, ", ") + "}"
+	case reflect.Complex64:
+		return "[complex64]"
+	case reflect.Complex128:
+		return "[complex128]"
+	case reflect.Chan:
+		return "[chan]"
+	case reflect.Func:
+		return "[func]"
+	case reflect.UnsafePointer:
+		return "[unsafeptr]"
 	default:
-		panic("unsupported type")
+		panic("unsupported value kind " + strconv.Itoa(int(rv.Kind())))
 	}
 }
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 6c5ed65..77b57d7 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -76,14 +76,14 @@
 	return m.delegate.getSize()
 }
 
-func (m *lazyMarshaler) marshal(v reflect.Value, out *encoder) error {
+func (m *lazyMarshaler) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	m.once.Do(m.init)
-	return m.delegate.marshal(v, out)
+	return m.delegate.marshal(ctx, v, out)
 }
 
-func (m *lazyMarshaler) unmarshal(in *decoder, v reflect.Value) error {
+func (m *lazyMarshaler) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	m.once.Do(m.init)
-	return m.delegate.unmarshal(in, v)
+	return m.delegate.unmarshal(ctx, in, v)
 }
 
 // Marshal marshals (or encodes) a message into the data and handles slices.
@@ -94,15 +94,16 @@
 	// code, it would fail in an obsure way withing relfection. Just don't do
 	// that.
 	var (
-		v = reflect.ValueOf(message).Elem()
-		m = message.Marshaler()
+		v   = reflect.ValueOf(message).Elem()
+		m   = message.Marshaler()
+		ctx = MarshalerContext{}
 	)
 
 	// Now, let's get the value of s, marshal the header into a starting
 	// buffer, and then marshal the rest of the payload in s.
 	out := &encoder{buffer: data[:0], handles: handles[:0]}
 	out.head = out.newObject(m.getSize())
-	if err := m.marshal(v, out); err != nil {
+	if err := m.marshal(ctx, v, out); err != nil {
 		return 0, 0, err
 	}
 	return len(out.buffer), len(out.handles), nil
@@ -111,6 +112,12 @@
 // Unmarshal unmarshals (or decodes) into message using the data and handles
 // slices.
 func Unmarshal(data []byte, handles []zx.Handle, message Message) (int, int, error) {
+	return UnmarshalWithContext(MarshalerContext{}, data, handles, message)
+}
+
+// UnmarshalWithConext is currently used only for transitioning from union to xunion.
+// The context, however, may be useful at a later date.
+func UnmarshalWithContext(ctx MarshalerContext, data []byte, handles []zx.Handle, message Message) (int, int, error) {
 	// By construction, we know that message is a pointer to a struct since
 	// we only generate pointer receiver methods for top-level messages.
 	// Should one implement the interface differently, and call into this
@@ -128,7 +135,7 @@
 		handles:    handles,
 		nextObject: nextObject,
 	}
-	if err := m.unmarshal(in, v); err != nil {
+	if err := m.unmarshal(ctx, in, v); err != nil {
 		return 0, 0, err
 	}
 	return in.nextObject, len(handles) - len(in.handles), nil
@@ -182,7 +189,7 @@
 		field := typ.Field(index)
 		_, fieldBounds := readTag(field)
 		switch kind {
-		case xunionTag, strictXunionTag, tableTag:
+		case unionTag, xunionTag, strictXunionTag, tableTag:
 			ordinal, actualBounds := fieldBounds.pop()
 			ordinals = append(ordinals, ordinal)
 			fieldBounds = actualBounds
@@ -215,6 +222,7 @@
 	case unionTag:
 		return mUnion{
 			fields:    fields,
+			ordinals:  ordinals,
 			size:      size,
 			alignment: alignment,
 		}, nil
@@ -407,10 +415,14 @@
 	Marshaler() Marshaler
 }
 
+type MarshalerContext struct {
+	DecodeUnionsFromXunionBytes bool
+}
+
 type Marshaler interface {
 	getSize() int
-	marshal(v reflect.Value, out *encoder) error
-	unmarshal(in *decoder, v reflect.Value) error
+	marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error
+	unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error
 }
 
 // Assert various encoders implement the Marshaler interface.
@@ -448,10 +460,10 @@
 	return m.size
 }
 
-func (m mStruct) marshal(v reflect.Value, out *encoder) error {
+func (m mStruct) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	out.head = align(out.head, m.alignment)
 	for _, field := range m.fields {
-		if err := field.Marshaler.marshal(v.Field(field.index), out); err != nil {
+		if err := field.Marshaler.marshal(ctx, v.Field(field.index), out); err != nil {
 			return err
 		}
 	}
@@ -459,10 +471,10 @@
 	return nil
 }
 
-func (m mStruct) unmarshal(in *decoder, v reflect.Value) error {
+func (m mStruct) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	in.head = align(in.head, m.alignment)
 	for _, field := range m.fields {
-		if err := field.Marshaler.unmarshal(in, v.Field(field.index)); err != nil {
+		if err := field.Marshaler.unmarshal(ctx, in, v.Field(field.index)); err != nil {
 			return err
 		}
 	}
@@ -476,12 +488,12 @@
 	return 1
 }
 
-func (_ mEmptyStruct) marshal(v reflect.Value, out *encoder) error {
+func (_ mEmptyStruct) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	out.writeUint(0, 1)
 	return nil
 }
 
-func (_ mEmptyStruct) unmarshal(in *decoder, v reflect.Value) error {
+func (_ mEmptyStruct) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	zero, err := in.readUint(1)
 	if err != nil {
 		return err
@@ -494,6 +506,7 @@
 
 type mUnion struct {
 	fields          []mField
+	ordinals        []int
 	size, alignment int
 }
 
@@ -501,7 +514,7 @@
 	return m.size
 }
 
-func (m mUnion) marshal(v reflect.Value, out *encoder) error {
+func (m mUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// Kind.
 	kind := int(v.Field(0).Uint()) - 1
 	if kind < 0 || len(m.fields) <= kind {
@@ -516,7 +529,7 @@
 	out.head = align(out.head, m.alignment)
 
 	// Marshal field.
-	if err := m.fields[kind].Marshaler.marshal(v.Field(kind+1), out); err != nil {
+	if err := m.fields[kind].Marshaler.marshal(ctx, v.Field(kind+1), out); err != nil {
 		return err
 	}
 
@@ -526,7 +539,26 @@
 	return nil
 }
 
-func (m mUnion) unmarshal(in *decoder, v reflect.Value) error {
+func (m mUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
+	if ctx.DecodeUnionsFromXunionBytes {
+		// XUnions have 24 bytes before the first out of line element.
+		in.nextObject = in.nextObject + 24 - m.getSize()
+		ordinalToFields := make(map[int]mField)
+		for i := 0; i < len(m.fields); i++ {
+			ordinalToFields[m.ordinals[i]] = m.fields[i]
+		}
+		// Just use the xunion unmarshaler because the layout of union and xunion
+		// is the same in go.
+		xunion := mXUnion{
+			fields:        ordinalToFields,
+			unionToXunion: true,
+			size:          m.size,
+			alignment:     m.alignment,
+			strictness:    true,
+		}
+		return xunion.unmarshal(ctx, in, v)
+	}
+
 	// Save the head for proper padding.
 	head := in.head
 
@@ -545,7 +577,7 @@
 
 	// Unmarshal field.
 	ikind := int(kind)
-	if err := m.fields[ikind].Marshaler.unmarshal(in, v.Field(ikind+1)); err != nil {
+	if err := m.fields[ikind].Marshaler.unmarshal(ctx, in, v.Field(ikind+1)); err != nil {
 		return err
 	}
 
@@ -557,6 +589,7 @@
 
 type mXUnion struct {
 	fields          map[int]mField
+	unionToXunion   bool
 	size, alignment int
 	strictness
 }
@@ -565,7 +598,7 @@
 	return m.size
 }
 
-func (m mXUnion) marshal(v reflect.Value, out *encoder) error {
+func (m mXUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// Ordinal.
 	ordinal := int(v.Field(0).Uint())
 	field, ok := m.fields[ordinal]
@@ -575,18 +608,18 @@
 	out.writeUint(uint64(ordinal), 8)
 
 	// Field.
-	if err := marshalEnvelopePresent(field, v.Field(field.index), out); err != nil {
+	if err := marshalEnvelopePresent(ctx, field, v.Field(field.index), out); err != nil {
 		return err
 	}
 
 	return nil
 }
 
-func (m mXUnion) unmarshal(in *decoder, v reflect.Value) error {
-	return m.unmarshalWithOptSpecified(in, v, nil)
+func (m mXUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
+	return m.unmarshalWithOptSpecified(ctx, in, v, nil)
 }
 
-func (m mXUnion) unmarshalWithOptSpecified(in *decoder, v reflect.Value, typ reflect.Type) error {
+func (m mXUnion) unmarshalWithOptSpecified(ctx MarshalerContext, in *decoder, v reflect.Value, typ reflect.Type) error {
 	// Can this envelope be optional? If it can, the type must be provided
 	// in order to reflectively create a pointer container.
 	optAllowed := typ != nil
@@ -655,7 +688,11 @@
 		v = v.Elem()
 	}
 
-	v.Field(0).SetUint(ordinal)
+	ordinalToSet := ordinal
+	if m.unionToXunion {
+		ordinalToSet = uint64(field.index)
+	}
+	v.Field(0).SetUint(ordinalToSet)
 
 	var mode unmarshalEnvelopeMode
 	if optAllowed {
@@ -664,7 +701,7 @@
 		mode = knownMustBePresent
 	}
 
-	isPresent, err := unmarshalEnvelope(field.Marshaler, in, v.Field(field.index), mode)
+	isPresent, err := unmarshalEnvelope(ctx, field.Marshaler, in, v.Field(field.index), mode)
 	if err != nil {
 		return err
 	}
@@ -685,18 +722,18 @@
 	return m.size
 }
 
-func (m mOptXUnion) marshal(v reflect.Value, out *encoder) error {
+func (m mOptXUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	if v.IsNil() {
 		out.writeUint(0, 8) // ordinal + padding
 		marshalEnvelopeAbsent(out)
 		return nil
 	} else {
-		return m.mXUnion.marshal(v.Elem(), out)
+		return m.mXUnion.marshal(ctx, v.Elem(), out)
 	}
 }
 
-func (m mOptXUnion) unmarshal(in *decoder, v reflect.Value) error {
-	return m.unmarshalWithOptSpecified(in, v, m.typ)
+func (m mOptXUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
+	return m.unmarshalWithOptSpecified(ctx, in, v, m.typ)
 }
 
 type mTable struct {
@@ -716,12 +753,12 @@
 	return h.presence == allocPresent
 }
 
-func marshalEnvelopePresent(m Marshaler, v reflect.Value, out *encoder) error {
+func marshalEnvelopePresent(ctx MarshalerContext, m Marshaler, v reflect.Value, out *encoder) error {
 	numHandles := len(out.handles)
 	numBytes := len(out.buffer)
 	head := out.head
 	out.head = out.newObject(m.getSize())
-	if err := m.marshal(v, out); err != nil {
+	if err := m.marshal(ctx, v, out); err != nil {
 		return err
 	}
 	numHandles = len(out.handles) - numHandles
@@ -814,11 +851,11 @@
 	return unknownData, nil
 }
 
-func unmarshalEnvelopeContent(header envelopeHeader, m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
+func unmarshalEnvelopeContent(ctx MarshalerContext, header envelopeHeader, m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
 	savedHead := in.head
 	in.head = in.nextObject
 	in.nextObject += align(m.getSize(), 8)
-	if err := m.unmarshal(in, v); err != nil {
+	if err := m.unmarshal(ctx, in, v); err != nil {
 		return false, err
 	}
 	in.head = savedHead
@@ -826,7 +863,7 @@
 	return true, nil
 }
 
-func unmarshalEnvelope(m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
+func unmarshalEnvelope(ctx MarshalerContext, m Marshaler, in *decoder, v reflect.Value, mode unmarshalEnvelopeMode) (bool, error) {
 	header, err := unmarshalEnvelopeHeader(in)
 	if err != nil {
 		return false, err
@@ -848,10 +885,10 @@
 		return false, nil
 	}
 
-	return unmarshalEnvelopeContent(header, m, in, v, mode)
+	return unmarshalEnvelopeContent(ctx, header, m, in, v, mode)
 }
 
-func (m mTable) marshal(v reflect.Value, out *encoder) error {
+func (m mTable) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// Determining max ordinal.
 	var (
 		maxOrdinal   int
@@ -889,7 +926,7 @@
 	for ordinal <= maxOrdinal {
 		fieldKnown := index < numKnown && ordinal == m.ordinals[index]
 		if fieldKnown && fieldPresent[index] {
-			if err := marshalEnvelopePresent(m.fields[index], v.Field(fieldIndex), out); err != nil {
+			if err := marshalEnvelopePresent(ctx, m.fields[index], v.Field(fieldIndex), out); err != nil {
 				return err
 			}
 		} else {
@@ -910,7 +947,7 @@
 	return nil
 }
 
-func (m mTable) unmarshal(in *decoder, v reflect.Value) error {
+func (m mTable) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	mou, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -954,7 +991,7 @@
 	for ordinal <= maxOrdinal {
 		fieldKnown := index < numKnown && ordinal == m.ordinals[index]
 		if fieldKnown {
-			if isPresent, err := unmarshalEnvelope(m.fields[index], in, v.Field(fieldIndex), knownMayBeAbsent); err != nil {
+			if isPresent, err := unmarshalEnvelope(ctx, m.fields[index], in, v.Field(fieldIndex), knownMayBeAbsent); err != nil {
 				return err
 			} else if isPresent {
 				v.Field(presenceIndex).SetBool(true)
@@ -985,7 +1022,7 @@
 	return 8
 }
 
-func (m mOptStructUnion) marshal(v reflect.Value, out *encoder) error {
+func (m mOptStructUnion) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// Nil?
 	if v.IsNil() {
 		out.writeUint(noAlloc, 8)
@@ -1000,7 +1037,7 @@
 	out.head = out.newObject(align(m.Marshaler.getSize(), 8))
 
 	// Marshal field.
-	if err := m.Marshaler.marshal(v.Elem(), out); err != nil {
+	if err := m.Marshaler.marshal(ctx, v.Elem(), out); err != nil {
 		return err
 	}
 
@@ -1010,7 +1047,7 @@
 	return nil
 }
 
-func (m mOptStructUnion) unmarshal(in *decoder, v reflect.Value) error {
+func (m mOptStructUnion) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	// Nil?
 	ptr, err := in.readUint(8)
 	if err != nil {
@@ -1035,7 +1072,7 @@
 	in.nextObject += align(m.Marshaler.getSize(), 8)
 
 	// Unmarshal field.
-	if err := m.Marshaler.unmarshal(in, v.Elem()); err != nil {
+	if err := m.Marshaler.unmarshal(ctx, in, v.Elem()); err != nil {
 		return err
 	}
 
@@ -1054,18 +1091,18 @@
 	return m.size * m.Marshaler.getSize()
 }
 
-func (m mArray) marshal(v reflect.Value, out *encoder) error {
+func (m mArray) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	for i, len := 0, v.Len(); i < len; i++ {
-		if err := m.Marshaler.marshal(v.Index(i), out); err != nil {
+		if err := m.Marshaler.marshal(ctx, v.Index(i), out); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-func (m mArray) unmarshal(in *decoder, v reflect.Value) error {
+func (m mArray) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	for i, len := 0, v.Len(); i < len; i++ {
-		if err := m.Marshaler.unmarshal(in, v.Index(i)); err != nil {
+		if err := m.Marshaler.unmarshal(ctx, in, v.Index(i)); err != nil {
 			return err
 		}
 	}
@@ -1083,7 +1120,7 @@
 	return 16
 }
 
-func (m mVector) marshal(v reflect.Value, out *encoder) error {
+func (m mVector) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// Bounds check.
 	vLen := v.Len()
 	if m.maxSize < vLen {
@@ -1108,7 +1145,7 @@
 		copy(out.buffer[out.head:], v.Bytes())
 	} else {
 		for i := 0; i < vLen; i++ {
-			if err := m.Marshaler.marshal(v.Index(i), out); err != nil {
+			if err := m.Marshaler.marshal(ctx, v.Index(i), out); err != nil {
 				return err
 			}
 		}
@@ -1120,7 +1157,7 @@
 	return nil
 }
 
-func (m mVector) unmarshal(in *decoder, v reflect.Value) error {
+func (m mVector) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -1133,13 +1170,13 @@
 	case noAlloc:
 		return newValueError(ErrUnexpectedNullRef, v)
 	case allocPresent:
-		return m.unmarshalWithUncheckedSize(in, v, int(size))
+		return m.unmarshalWithUncheckedSize(ctx, in, v, int(size))
 	default:
 		return newValueError(ErrBadRefEncoding, v)
 	}
 }
 
-func (m mVector) unmarshalWithUncheckedSize(in *decoder, v reflect.Value, size int) error {
+func (m mVector) unmarshalWithUncheckedSize(ctx MarshalerContext, in *decoder, v reflect.Value, size int) error {
 	if size < 0 || m.maxSize < size {
 		return newExpectError(ErrVectorTooLong, m.maxSize, size)
 	}
@@ -1158,7 +1195,7 @@
 	} else {
 		v.Set(reflect.MakeSlice(m.sliceTyp, size, size))
 		for i := 0; i < size; i++ {
-			if err := m.Marshaler.unmarshal(in, v.Index(i)); err != nil {
+			if err := m.Marshaler.unmarshal(ctx, in, v.Index(i)); err != nil {
 				return err
 			}
 		}
@@ -1176,17 +1213,17 @@
 	return 16
 }
 
-func (m mOptVector) marshal(v reflect.Value, out *encoder) error {
+func (m mOptVector) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	if v.IsNil() {
 		out.writeUint(0, 8)
 		out.writeUint(noAlloc, 8)
 		return nil
 	}
 
-	return mVector(m).marshal(v.Elem(), out)
+	return mVector(m).marshal(ctx, v.Elem(), out)
 }
 
-func (m mOptVector) unmarshal(in *decoder, v reflect.Value) error {
+func (m mOptVector) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -1201,7 +1238,7 @@
 		return nil
 	case allocPresent:
 		v.Set(reflect.New(m.sliceTyp))
-		return mVector(m).unmarshalWithUncheckedSize(in, v.Elem(), int(size))
+		return mVector(m).unmarshalWithUncheckedSize(ctx, in, v.Elem(), int(size))
 	default:
 		return newValueError(ErrBadRefEncoding, v)
 	}
@@ -1213,7 +1250,7 @@
 	return 1
 }
 
-func (m mBool) marshal(v reflect.Value, out *encoder) error {
+func (m mBool) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	if v.Bool() {
 		out.writeUint(1, 1)
 	} else {
@@ -1222,7 +1259,7 @@
 	return nil
 }
 
-func (m mBool) unmarshal(in *decoder, v reflect.Value) error {
+func (m mBool) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	b, err := in.readUint(1)
 	if err != nil {
 		return err
@@ -1243,13 +1280,13 @@
 	return int(m)
 }
 
-func (m mInt) marshal(v reflect.Value, out *encoder) error {
+func (m mInt) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	size := int(m)
 	out.writeUint(uint64(v.Int()), size)
 	return nil
 }
 
-func (m mInt) unmarshal(in *decoder, v reflect.Value) error {
+func (m mInt) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size := int(m)
 	val, err := in.readUint(size)
 	if err != nil {
@@ -1266,13 +1303,13 @@
 	return int(m)
 }
 
-func (m mUint) marshal(v reflect.Value, out *encoder) error {
+func (m mUint) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	size := int(m)
 	out.writeUint(v.Uint(), size)
 	return nil
 }
 
-func (m mUint) unmarshal(in *decoder, v reflect.Value) error {
+func (m mUint) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size := int(m)
 	val, err := in.readUint(size)
 	if err != nil {
@@ -1288,12 +1325,12 @@
 	return 4
 }
 
-func (m mFloat32) marshal(v reflect.Value, out *encoder) error {
+func (m mFloat32) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	out.writeUint(uint64(math.Float32bits(float32(v.Float()))), 4)
 	return nil
 }
 
-func (m mFloat32) unmarshal(in *decoder, v reflect.Value) error {
+func (m mFloat32) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	val, err := in.readUint(4)
 	if err != nil {
 		return err
@@ -1308,12 +1345,12 @@
 	return 8
 }
 
-func (m mFloat64) marshal(v reflect.Value, out *encoder) error {
+func (m mFloat64) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	out.writeUint(math.Float64bits(v.Float()), 8)
 	return nil
 }
 
-func (m mFloat64) unmarshal(in *decoder, v reflect.Value) error {
+func (m mFloat64) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	val, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -1328,7 +1365,7 @@
 	return 16
 }
 
-func (m mString) marshal(v reflect.Value, out *encoder) error {
+func (m mString) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	var (
 		maxSize = int(m)
 		s       = v.String()
@@ -1349,7 +1386,7 @@
 	return nil
 }
 
-func (m mString) unmarshal(in *decoder, v reflect.Value) error {
+func (m mString) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -1391,14 +1428,14 @@
 	return 16
 }
 
-func (m mOptString) marshal(v reflect.Value, out *encoder) error {
+func (m mOptString) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	if v.IsNil() {
 		out.writeUint(0, 8)
 		out.writeUint(noAlloc, 8)
 		return nil
 	}
 
-	return mString(m).marshal(v.Elem(), out)
+	return mString(m).marshal(ctx, v.Elem(), out)
 }
 
 var (
@@ -1406,7 +1443,7 @@
 	typString = reflect.TypeOf("")
 )
 
-func (m mOptString) unmarshal(in *decoder, v reflect.Value) error {
+func (m mOptString) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	size, err := in.readUint(8)
 	if err != nil {
 		return err
@@ -1437,7 +1474,7 @@
 	return bool(m)
 }
 
-func (m mHandle) marshal(v reflect.Value, out *encoder) error {
+func (m mHandle) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
 	// The underlying type of all the handles is a uint32, so we're
 	// safe calling Uint(). This will panic if that is no longer true.
 	raw := zx.Handle(v.Uint())
@@ -1453,7 +1490,7 @@
 	return nil
 }
 
-func (m mHandle) unmarshal(in *decoder, v reflect.Value) error {
+func (m mHandle) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
 	h, err := in.readUint(4)
 	if err != nil {
 		return err
@@ -1487,10 +1524,10 @@
 	return mHandle(m).getSize()
 }
 
-func (m mInterface) marshal(v reflect.Value, out *encoder) error {
-	return mHandle(m).marshal(v.Field(0), out)
+func (m mInterface) marshal(ctx MarshalerContext, v reflect.Value, out *encoder) error {
+	return mHandle(m).marshal(ctx, v.Field(0), out)
 }
 
-func (m mInterface) unmarshal(in *decoder, v reflect.Value) error {
-	return mHandle(m).unmarshal(in, v.Field(0))
+func (m mInterface) unmarshal(ctx MarshalerContext, in *decoder, v reflect.Value) error {
+	return mHandle(m).unmarshal(ctx, in, v.Field(0))
 }
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
index 9801c37..e184e6b 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
@@ -837,6 +837,38 @@
 	}
 }
 
+func TestUnmarshalXunionIntoUnion(t *testing.T) {
+	const expectedUnionTag = 3 // float32 field
+	const expectedFieldValue = 1.2
+
+	var respb [zx.ChannelMaxMessageBytes]byte
+	var resph [zx.ChannelMaxMessageHandles]zx.Handle
+	input := XUnion1Struct{Xu: XUnion1WithD(expectedFieldValue)}
+	_, _, err := Marshal(&input, respb[:], resph[:])
+	if err != nil {
+		t.Fatalf("error unmarshaling: %v\n", err)
+	}
+	// Set the ordinal value to the value in XUnion1AsUnionStruct, since it is
+	// a different type.
+	// This value comes from the generated JSON for test.test.fidl.
+	respb[0] = 0x24
+	respb[1] = 0x94
+	respb[2] = 0x6a
+	respb[3] = 0x3e
+	ctx := MarshalerContext{DecodeUnionsFromXunionBytes: true}
+	var union XUnion1AsUnionStruct
+	_, _, err = UnmarshalWithContext(ctx, respb[:], nil, &union)
+	if err != nil {
+		t.Fatalf("error marshaling: %v", err)
+	}
+	if union.Xuau.I_xUnion1AsUnionTag != expectedUnionTag {
+		t.Fatalf("want %v, got %v", expectedUnionTag, union.Xuau.I_xUnion1AsUnionTag)
+	}
+	if union.Xuau.D != expectedFieldValue {
+		t.Fatalf("want %v, got %v", expectedFieldValue, union.Xuau.D)
+	}
+}
+
 func benchmarkMarshal(b *testing.B, ex example) {
 	b.Run(ex.name, func(b *testing.B) {
 		b.StopTimer()
diff --git a/src/syscall/zx/fidl/message.go b/src/syscall/zx/fidl/message.go
index dcb57b9..ecc85c9 100644
--- a/src/syscall/zx/fidl/message.go
+++ b/src/syscall/zx/fidl/message.go
@@ -74,8 +74,16 @@
 	if body == nil {
 		return nil
 	}
-	if _, _, err := Unmarshal(data[hnb:], handles, body); err != nil {
+	ctx := MarshalerContext{
+		DecodeUnionsFromXunionBytes: shouldDecodeUnionsFromXunionBytes(header),
+	}
+	if _, _, err := UnmarshalWithContext(ctx, data[hnb:], handles, body); err != nil {
 		return err
 	}
 	return nil
 }
+
+func shouldDecodeUnionsFromXunionBytes(header *MessageHeader) bool {
+	// TODO(fxb/37361) Implement this predicate.
+	return false
+}
diff --git a/src/syscall/zx/io/impl.go b/src/syscall/zx/io/impl.go
index bd04550..9d94b82 100644
--- a/src/syscall/zx/io/impl.go
+++ b/src/syscall/zx/io/impl.go
@@ -399,14 +399,14 @@
 // Refer to `Node::Describe()` and `Node::OnOpen()` for usage.
 type NodeInfo struct {
 	I_nodeInfoTag `fidl:"u,32,8" fidl2:"u,32,8"`
-	Service       Service
-	File          FileObject
-	Directory     DirectoryObject
-	Pipe          Pipe
-	Vmofile       Vmofile
-	Device        Device
-	Tty           Tty
-	Socket        Socket
+	Service       Service         `fidl:"1116800047" fidl2:"1116800047"`
+	File          FileObject      `fidl:"525060263" fidl2:"525060263"`
+	Directory     DirectoryObject `fidl:"1775777620" fidl2:"1775777620"`
+	Pipe          Pipe            `fidl:"1930052674" fidl2:"1930052674"`
+	Vmofile       Vmofile         `fidl:"1455689042" fidl2:"1455689042"`
+	Device        Device          `fidl:"75663405" fidl2:"75663405"`
+	Tty           Tty             `fidl:"1991666602" fidl2:"1991666602"`
+	Socket        Socket          `fidl:"1568464479" fidl2:"1568464479"`
 }
 
 func (u *NodeInfo) Which() I_nodeInfoTag {