Bug 1438688, part 6 - Compile XPT information to C++ at build time. r=glandium,njn
authorAndrew McCreight <continuation@gmail.com>
Mon, 12 Mar 2018 10:30:35 -0700
changeset 776572 102872860c7ab710800251aac7814bb5bb1168f9
parent 776571 f1dd107a1ad42d8bb46574e5f23965316991c836
child 776573 5c9c6f5a4ca35835b9614a8cdb3eefda44cb585d
push id104907
push userbmo:ato@sny.no
push dateTue, 03 Apr 2018 10:28:20 +0000
reviewersglandium, njn
bugs1438688
milestone61.0a1
Bug 1438688, part 6 - Compile XPT information to C++ at build time. r=glandium,njn This patch handles the actual generation of the static data structures used to represent XPT information. XPT files are generated in the same way as they are now, but they are used only as an intermediate representation to speed up incremental compilation rather than something used by Firefox itself. Instead of linking XPTs into a single big XPT file at packaging time, they are linked into a single big C++ file at build time, that defines the various static consts in XPTHeader. In xpt.py, every data structure that can get written to disk gets an additional code_gen() method that returns a representation of that data structure as C++ source code. CodeGenData aggregates this information together, handling deduplication and the final source code generation. The ctors are needed for XPTConstValue to statically initialize the different union cases without resorting to designated initializers, which are part of C99, not C++. Designated initializers appear to be supported in C++ code by Clang and GCC, but not MSVC. The ctors must be constexpr to ensure they are actually statically initialized so they can be shared between Firefox processes. I also removed an unnecessary "union" in XPTConstDescriptor. Together, these patches reduce the amount of memory reported by xpti-working-set from about 860,000 bytes to about 200,000 bytes. The remaining memory is used for xptiInterface and xptiTypelibGuts (which are thin wrappers around the XPT interfaces and header) and hash tables to speed up looking up interfaces by name or IID. That could potentially be eliminated from dynamic allocations in follow up work. These patches did not affect memory reporting because XPT arenas are still used by the remaining XPTI data structures. MozReview-Commit-ID: Jvi9ByCPa6H
config/makefiles/xpidl/Makefile.in
xpcom/typelib/xpt/moz.build
xpcom/typelib/xpt/tools/xpt.py
xpcom/typelib/xpt/xpt_struct.h
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -25,16 +25,18 @@ include $(topsrcdir)/config/rules.mk
 
 # For dependency files.
 idl_deps_dir := .deps
 
 dist_idl_dir := $(DIST)/idl
 dist_include_dir := $(DIST)/include
 dist_xpcrs_dir := $(DIST)/xpcrs
 process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
+generated_file := $(topobjdir)/xpcom/typelib/xpt/XPTInfo.cpp
+code_gen_py := $(topsrcdir)/xpcom/typelib/xpt/tools/xpt.py
 
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
 	$(REPORT_BUILD)
 	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
 		$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
 		$(dist_idl_dir) $(dist_include_dir) $(dist_xpcrs_dir) $(@D) \
@@ -50,24 +52,30 @@ process_py := $(topsrcdir)/python/mozbui
 
 xpidl_modules := @xpidl_modules@
 xpt_files := $(addsuffix .xpt,$(xpidl_modules))
 
 @xpidl_rules@
 
 depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp)
 
-GARBAGE += $(xpt_files) $(depends_files)
+GARBAGE += $(xpt_files) $(depends_files) $(generated_file)
 
 ifdef COMPILE_ENVIRONMENT
-xpidl:: $(xpt_files)
+xpidl:: $(generated_file)
 endif
 
 $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir) $(dist_xpcrs_dir))
 
+$(generated_file): $(xpt_files) $(code_gen_py)
+	$(REPORT_BUILD)
+	$(PYTHON_PATH) $(PLY_INCLUDE) \
+		$(code_gen_py) linkgen \
+		$(generated_file) $(xpt_files)
+
 -include $(depends_files)
 
 define xpt_deps
 $(1): $(call mkdir_deps,$(dir $(1)))
 $(1): $(addsuffix .idl,$(addprefix $(dist_idl_dir)/,$($(basename $(notdir $(1)))_deps)))
 ifneq ($($(basename $(notdir $(1)))_deps),$($(basename $(notdir $(1)))_deps_built))
 $(1): FORCE
 endif
--- a/xpcom/typelib/xpt/moz.build
+++ b/xpcom/typelib/xpt/moz.build
@@ -7,16 +7,20 @@
 Library('xpt')
 
 DIRS += ['tools']
 
 UNIFIED_SOURCES += [
     'xpt_arena.cpp',
 ]
 
+SOURCES += [
+    '!XPTInfo.cpp',
+]
+
 EXPORTS += [
     'xpt_arena.h',
     'xpt_struct.h',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
--- a/xpcom/typelib/xpt/tools/xpt.py
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -29,19 +29,19 @@
 # are those of the authors and should not be interpreted as representing
 # official policies, either expressed or implied, of the Mozilla
 # Foundation.
 
 """
 A module for working with XPCOM Type Libraries.
 
 The XPCOM Type Library File Format is described at:
-http://www.mozilla.org/scriptable/typelib_file.html . It is used
-to provide type information for calling methods on XPCOM objects
-from scripting languages such as JavaScript.
+  https://www-archive.mozilla.org/scriptable/typelib_file.html
+It is used to provide type information for calling methods on XPCOM
+objects from scripting languages such as JavaScript.
 
 This module provides a set of classes representing the parts of
 a typelib in a high-level manner, as well as methods for reading
 and writing them from files.
 
 The usable public interfaces are currently:
 Typelib.read(input_file) - read a typelib from a file on disk or file-like
                            object, return a Typelib object.
@@ -129,16 +129,133 @@ class IndexedList(object):
 
     def __getitem__(self, index):
         return self._list[index]
 
     def __len__(self):
         return len(self._list)
 
 
+class CodeGenData(object):
+    """
+    This stores the top-level data needed to generate XPT information in C++.
+    |methods| and |constants| are the top-level declarations in the module.
+    These contain names, and so are not likely to benefit from deduplication.
+    |params| are the lists of parameters for |methods|, stored concatenated.
+    These are deduplicated if there are only a few. |types| and |strings| are
+    side data stores for the other things, and are deduplicated.
+
+    """
+
+    def __init__(self):
+        self.interfaces = []
+
+        self.types = []
+        self.type_indexes = {}
+
+        self.params = []
+        self.params_indexes = {}
+
+        self.methods = []
+
+        self.constants = []
+
+        self.strings = []
+        self.string_indexes = {}
+        self.curr_string_index = 0
+
+    @staticmethod
+    def write_array_body(fd, iterator):
+        fd.write("{\n")
+        for s in iterator:
+            fd.write("  %s,\n" % s)
+        fd.write("};\n\n")
+
+    def finish(self, fd):
+        fd.write("const uint16_t XPTHeader::kNumInterfaces = %s;\n\n" % len(self.interfaces))
+
+        fd.write("const XPTInterfaceDescriptor XPTHeader::kInterfaces[] = ")
+        CodeGenData.write_array_body(fd, self.interfaces)
+
+        fd.write("const XPTTypeDescriptor XPTHeader::kTypes[] = ")
+        CodeGenData.write_array_body(fd, self.types)
+
+        fd.write("const XPTParamDescriptor XPTHeader::kParams[] = ")
+        CodeGenData.write_array_body(fd, self.params)
+
+        fd.write("const XPTMethodDescriptor XPTHeader::kMethods[] = ")
+        CodeGenData.write_array_body(fd, self.methods)
+
+        fd.write("const XPTConstDescriptor XPTHeader::kConsts[] = ")
+        CodeGenData.write_array_body(fd, self.constants)
+
+        fd.write("const char XPTHeader::kStrings[] = {\n")
+        if self.strings:
+            for s in self.strings:
+                # Store each string as individual characters to work around
+                # MSVC's limit of 65k characters for a single string literal
+                # (error C1091).
+                s_index = self.string_indexes[s]
+                fd.write("    '%s', '\\0', // %s %d\n" % ("', '".join(list(s)), s, s_index))
+        else:
+            fd.write('""')
+        fd.write('};\n\n')
+
+    def add_interface(self, new_interface):
+        assert new_interface
+        self.interfaces.append(new_interface)
+
+    def add_type(self, new_type):
+        assert isinstance(new_type, basestring)
+        if new_type in self.type_indexes:
+            return self.type_indexes[new_type]
+        index = len(self.types)
+        self.types.append(new_type)
+        self.type_indexes[new_type] = index
+        return index
+
+    def add_params(self, new_params):
+        # Always represent empty parameter lists as being at 0, for no
+        # particular reason beside it being nicer.
+        if len(new_params) == 0:
+            return 0
+
+        index = len(self.params)
+        # The limit of 4 here is fairly arbitrary. The idea is to not
+        # spend time adding large things to the cache that have little
+        # chance of getting used again.
+        if len(new_params) <= 4:
+            params_key = "".join(new_params)
+            if params_key in self.params_indexes:
+                return self.params_indexes[params_key]
+            else:
+                self.params_indexes[params_key] = index
+        self.params += new_params
+        return index
+
+    def add_methods(self, new_methods):
+        index = len(self.methods)
+        self.methods += new_methods
+        return index
+
+    def add_constants(self, new_constants):
+        index = len(self.constants)
+        self.constants += new_constants
+        return index
+
+    def add_string(self, new_string):
+        if new_string in self.string_indexes:
+            return self.string_indexes[new_string]
+        index = self.curr_string_index
+        self.strings.append(new_string)
+        self.string_indexes[new_string] = index
+        self.curr_string_index += len(new_string) + 1
+        return index
+
+
 # Descriptor types as described in the spec
 class Type(object):
     """
     Data type of a method parameter or return value. Do not instantiate
     this class directly. Rather, use one of its subclasses.
 
     """
     _prefixdescriptor = struct.Struct(">B")
@@ -258,16 +375,23 @@ class Type(object):
         Write a TypeDescriptor to |file|, which is assumed
         to be seeked to the proper position. For types other than
         SimpleType, this is not sufficient for writing the TypeDescriptor,
         and the subclass method must be called.
 
         """
         file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
 
+    def typeDescriptorPrefixString(self):
+        """
+        Return a string for the C++ code to represent the XPTTypeDescriptorPrefix.
+
+        """
+        return "{0x%x}" % (self.encodeflags() | self.tag)
+
 
 class SimpleType(Type):
     """
     A simple data type. (SimpleTypeDescriptor from the typelib specification.)
 
     """
     _cache = {}
 
@@ -306,16 +430,19 @@ class SimpleType(Type):
 
         if self.pointer:
             if self.reference:
                 s += " &"
             else:
                 s += " *"
         return s
 
+    def code_gen(self, typelib, cd):
+        return "{%s, 0, 0}" % self.typeDescriptorPrefixString()
+
 
 class InterfaceType(Type):
     """
     A type representing a pointer to an IDL-defined interface.
     (InterfaceTypeDescriptor from the typelib specification.)
 
     """
     _descriptor = struct.Struct(">H")
@@ -361,16 +488,22 @@ class InterfaceType(Type):
         Write an InterfaceTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         # write out the interface index (1-based)
         file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
 
+    def code_gen(self, typelib, cd):
+        index = typelib.interfaces.index(self.iface) + 1
+        hi = int(index / 256)
+        lo = index - (hi * 256)
+        return "{%s, %d, %d}" % (self.typeDescriptorPrefixString(), hi, lo)
+
     def __str__(self):
         if self.iface:
             return self.iface.name
         return "unknown interface"
 
 
 class InterfaceIsType(Type):
     """
@@ -420,16 +553,20 @@ class InterfaceIsType(Type):
         """
         Write an InterfaceIsTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(InterfaceIsType._descriptor.pack(self.param_index))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.param_index)
+
     def __str__(self):
         return "InterfaceIs *"
 
 
 class ArrayType(Type):
     """
     A type representing an Array of elements of another type, whose
     size and length are passed as separate parameters to a method.
@@ -480,16 +617,22 @@ class ArrayType(Type):
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
                                               self.length_is_arg_num))
         self.element_type.write(typelib, file)
 
+    def code_gen(self, typelib, cd):
+        element_type_index = cd.add_type(self.element_type.code_gen(typelib, cd))
+        return "{%s, %d, %d}" % (self.typeDescriptorPrefixString(),
+                                 self.size_is_arg_num,
+                                 element_type_index)
+
     def __str__(self):
         return "%s []" % str(self.element_type)
 
 
 class StringWithSizeType(Type):
     """
     A type representing a UTF-8 encoded string whose size and length
     are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
@@ -536,16 +679,20 @@ class StringWithSizeType(Type):
         Write a StringWithSizeTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
                                                        self.length_is_arg_num))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.size_is_arg_num)
+
     def __str__(self):
         return "string_s"
 
 
 class WideStringWithSizeType(Type):
     """
     A type representing a UTF-16 encoded string whose size and length
     are passed as separate arguments to a method.
@@ -592,16 +739,20 @@ class WideStringWithSizeType(Type):
         Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
                                                            self.length_is_arg_num))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.size_is_arg_num)
+
     def __str__(self):
         return "wstring_s"
 
 
 class CachedStringWriter(object):
     """
     A cache that sits in front of a file to avoid adding the same
     string multiple times.
@@ -720,16 +871,19 @@ class Param(object):
         """
         Write a ParamDescriptor to |file|, which is assumed to be seeked
         to the correct position.
 
         """
         file.write(Param._descriptorstart.pack(self.encodeflags()))
         self.type.write(typelib, file)
 
+    def code_gen(self, typelib, cd):
+        return "{0x%x, %s}" % (self.encodeflags(), self.type.code_gen(typelib, cd))
+
     def prefix(self):
         """
         Return a human-readable string representing the flags set
         on this Param.
 
         """
         s = ""
         if self.out:
@@ -900,29 +1054,48 @@ class Method(object):
         """
         Write this method's name to |string_writer|'s file.
         Assumes that this file is currently seeked to an unused
         portion of the data pool.
 
         """
         self._name_offset = string_writer.write(self.name)
 
+    def code_gen(self, typelib, cd):
+        # Don't store any extra info for methods that can't be called from JS.
+        if self.notxpcom or self.hidden:
+            string_index = 0
+            param_index = 0
+            num_params = 0
+        else:
+            string_index = cd.add_string(self.name)
+            param_index = cd.add_params([p.code_gen(typelib, cd) for p in self.params])
+            num_params = len(self.params)
+
+        return "{%d, %d, 0x%x, %d}" % (string_index,
+                                       param_index,
+                                       self.encodeflags(),
+                                       num_params)
 
 class Constant(object):
     """
     A constant value of a specific type defined on an interface.
     (ConstantDescriptor from the typelib specification.)
 
     """
     _descriptorstart = struct.Struct(">I")
     # Actual value is restricted to this set of types
     typemap = {Type.Tags.int16: '>h',
                Type.Tags.uint16: '>H',
                Type.Tags.int32: '>i',
                Type.Tags.uint32: '>I'}
+    memberTypeMap = {Type.Tags.int16: 'int16_t',
+                     Type.Tags.uint16: 'uint16_t',
+                     Type.Tags.int32: 'int32_t',
+                     Type.Tags.uint32: 'uint32_t'}
 
     def __init__(self, name, type, value):
         self.name = name
         self._name_offset = 0
         self.type = type
         self.value = value
 
     def __cmp__(self, other):
@@ -971,16 +1144,25 @@ class Constant(object):
         """
         Write this constants's name to |string_writer|'s file.
         Assumes that this file is currently seeked to an unused
         portion of the data pool.
 
         """
         self._name_offset = string_writer.write(self.name)
 
+    def code_gen(self, typelib, cd):
+        string_index = cd.add_string(self.name)
+
+        # The static cast is needed for disambiguation.
+        return "{%d, %s, XPTConstValue(static_cast<%s>(%d))}" % (string_index,
+                                                                 self.type.code_gen(typelib, cd),
+                                                                 Constant.memberTypeMap[self.type.tag],
+                                                                 self.value)
+
     def __repr__(self):
         return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
 
 
 class Interface(object):
     """
     An Interface represents an object, with its associated methods
     and constant values.
@@ -1166,16 +1348,45 @@ class Interface(object):
         """
         self._name_offset = string_writer.write(self.name)
         self._namespace_offset = string_writer.write(self.namespace)
         for m in self.methods:
             m.write_name(string_writer)
         for c in self.constants:
             c.write_name(string_writer)
 
+    def code_gen_interface(self, typelib, cd):
+        iid = Typelib.code_gen_iid(self.iid)
+        string_index = cd.add_string(self.name)
+
+        parent_idx = 0
+        if self.resolved:
+            methods_index = cd.add_methods([m.code_gen(typelib, cd) for m in self.methods])
+            constants_index = cd.add_constants([c.code_gen(typelib, cd) for c in self.constants])
+            if self.parent:
+                parent_idx = typelib.interfaces.index(self.parent) + 1
+        else:
+            # Unresolved interfaces only have their name and IID set to non-zero values.
+            methods_index = 0
+            constants_index = 0
+            assert len(self.methods) == 0
+            assert len(self.constants) == 0
+            assert self.encodeflags() == 0
+
+        return "{%s, %s, %d, %d, %d, %d, %d, 0x%x} /* %s */" % (
+            iid,
+            string_index,
+            methods_index,
+            constants_index,
+            parent_idx,
+            len(self.methods),
+            len(self.constants),
+            self.encodeflags(),
+            self.name)
+
 
 class Typelib(object):
     """
     A typelib represents one entire typelib file and all the interfaces
     referenced within, whether defined entirely within the typelib or
     merely referenced by name or IID.
 
     Typelib objects may be instantiated directly and populated with data,
@@ -1283,16 +1494,26 @@ class Typelib(object):
             iface = Interface(name, iid, namespace)
             iface._descriptor_offset = ide[3]
             iface.xpt_filename = xpt.filename
             xpt.interfaces.append(iface)
         for iface in xpt.interfaces:
             iface.read_descriptor(xpt, data, data_pool_offset)
         return xpt
 
+    @staticmethod
+    def code_gen_iid(iid):
+        chunks = iid.split('-')
+        return "{0x%s, 0x%s, 0x%s, {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}}" % (
+            chunks[0], chunks[1], chunks[2],
+            int(chunks[3][0:2], 16), int(chunks[3][2:4], 16),
+            int(chunks[4][0:2], 16), int(chunks[4][2:4], 16),
+            int(chunks[4][4:6], 16), int(chunks[4][6:8], 16),
+            int(chunks[4][8:10], 16), int(chunks[4][10:12], 16))
+
     def __repr__(self):
         return "<Typelib with %d interfaces>" % len(self.interfaces)
 
     def _sanityCheck(self):
         """
         Check certain assumptions about data contained in this typelib.
         Sort the interfaces array by IID, check that all interfaces
         referenced by methods exist in the array.
@@ -1357,16 +1578,49 @@ class Typelib(object):
         """
         self._sanityCheck()
         if isinstance(output_file, basestring):
             with open(output_file, "wb") as f:
                 self.writefd(f)
         else:
             self.writefd(output_file)
 
+    def code_gen_writefd(self, fd):
+        cd = CodeGenData()
+
+        for i in self.interfaces:
+            cd.add_interface(i.code_gen_interface(self, cd))
+
+        fd.write("""/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. */
+
+#include "xpt_struct.h"
+
+""")
+        cd.finish(fd)
+
+    def code_gen_write(self, output_file):
+        """
+        Write the contents of this typelib to |output_file|,
+        which can be either a filename or a file-like object.
+
+        """
+        self._sanityCheck()
+
+        if isinstance(output_file, basestring):
+            with open(output_file, "wb") as f:
+                self.code_gen_writefd(f)
+        else:
+            self.code_gen_writefd(output_file)
+
     def dump(self, out):
         """
         Print a human-readable listing of the contents of this typelib
         to |out|, in the format of xpt_dump.
 
         """
         out.write("""Header:
    Major version:         %d
@@ -1574,14 +1828,16 @@ def xpt_link(inputs):
     interfaces = list(required_interfaces)
 
     # Re-sort interfaces (by IID)
     interfaces.sort()
     return Typelib(interfaces=interfaces)
 
 if __name__ == '__main__':
     if len(sys.argv) < 3:
-        print >>sys.stderr, "xpt <dump|link> <files>"
+        print >>sys.stderr, "xpt <dump|link|linkgen> <files>"
         sys.exit(1)
     if sys.argv[1] == 'dump':
         xpt_dump(sys.argv[2])
     elif sys.argv[1] == 'link':
         xpt_link(sys.argv[3:]).write(sys.argv[2])
+    elif sys.argv[1] == 'linkgen':
+        xpt_link(sys.argv[3:]).code_gen_write(sys.argv[2])
--- a/xpcom/typelib/xpt/xpt_struct.h
+++ b/xpcom/typelib/xpt/xpt_struct.h
@@ -173,26 +173,35 @@ struct XPTTypeDescriptor {
  * TypeDescriptor record. For instance, if type corresponds to int16_t, then
  * value is a 16-bit signed integer.
  */
 union XPTConstValue {
   int16_t i16;
   uint16_t ui16;
   int32_t i32;
   uint32_t ui32;
+
+  // These constructors are needed to statically initialize different cases of
+  // the union because MSVC does not support the use of designated initializers
+  // in C++ code. They need to be constexpr to ensure that no initialization code
+  // is run at startup, to enable sharing of this memory between Firefox processes.
+  explicit constexpr XPTConstValue(int16_t aInt) : i16(aInt) {}
+  explicit constexpr XPTConstValue(uint16_t aInt) : ui16(aInt) {}
+  explicit constexpr XPTConstValue(int32_t aInt) : i32(aInt) {}
+  explicit constexpr XPTConstValue(uint32_t aInt) : ui32(aInt) {}
 };
 
 struct XPTConstDescriptor {
   const char* Name() const {
     return &XPTHeader::kStrings[mName];
   }
 
   uint32_t mName; // Index into XPTHeader::mStrings.
   XPTTypeDescriptor mType;
-  union XPTConstValue mValue;
+  XPTConstValue mValue;
 };
 
 /*
  * A ParamDescriptor is used to describe either a single argument to a method or
  * a method's result.
  */
 struct XPTParamDescriptor {
   uint8_t mFlags;