Bug 1523996 - part 4 - add static assertions for member layout; r=Alex_Gaynor
authorNathan Froyd <froydnj@gmail.com>
Wed, 06 Mar 2019 13:59:48 +0000
changeset 520542 90d17458ca5425d50a0d58ad59ec227a3458890e
parent 520541 1746c87bdd261f2254909bb25d0c15091ec1e545
child 520543 608ca009efdfd35fc49127b47bb13a57df6f7273
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersAlex_Gaynor
bugs1523996
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1523996 - part 4 - add static assertions for member layout; r=Alex_Gaynor We're about to start depending on how the fields are packed in a future patch, so we should add some compile-time checking that our assertions are correct. Differential Revision: https://phabricator.services.mozilla.com/D22000
ipc/ipdl/ipdl/lower.py
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -1,15 +1,16 @@
 # 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/.
 
 import re
 from copy import deepcopy
 from collections import OrderedDict
+import itertools
 
 import ipdl.ast
 import ipdl.builtin
 from ipdl.cxx.ast import *
 from ipdl.direct_call import VIRTUAL_CALL_CLASSES, DIRECT_CALL_OVERRIDES
 from ipdl.type import ActorType, UnionType, TypeVisitor, builtinHeaderIncludes
 
 
@@ -2291,16 +2292,47 @@ before this struct.  Some types generate
 
     def visitMessageType(self, v): assert 0
 
     def visitProtocolType(self, v): assert 0
 
     def visitStateType(self, v): assert 0
 
 
+def _fieldStaticAssertions(sd):
+    staticasserts = []
+    for (size, fields) in itertools.groupby(sd.fields_member_order(),
+                                            lambda f: pod_size(f.ipdltype)):
+        if size == pod_size_sentinel:
+            continue
+
+        fields = list(fields)
+        if len(fields) == 1:
+            continue
+
+        # If we get here, we have a list of fields of some certain size that we
+        # would like to assume are laid out consecutively in memory with no
+        # padding between fields.  The types of the fields (integers, booleans,
+        # and floats) are such that we don't have to worry about internal
+        # padding in the fields themselves.  We check our assumptions via
+        # static_assert.
+        start_offset = ExprCall(ExprVar('offsetof'),
+                                args=[ExprVar(sd.name), fields[0].memberVar()])
+        end_offset = ExprCall(ExprVar('offsetof'),
+                              args=[ExprVar(sd.name), fields[-1].memberVar()])
+        diff = ExprBinary(end_offset, '-', start_offset)
+        eqcheck = ExprBinary(diff, '==', ExprLiteral.Int(size * (len(fields) - 1)))
+        assert_ = ExprCall(ExprVar('static_assert'),
+                           args=[eqcheck,
+                                 ExprLiteral.String("Bad assumptions about field layout!")])
+        staticasserts.append(StmtExpr(assert_))
+
+    return staticasserts
+
+
 def _generateCxxStruct(sd):
     ''' '''
     # compute all the typedefs and forward decls we need to make
     gettypedeps = _ComputeTypeDeps(sd.decl.type)
     for f in sd.fields:
         f.ipdltype.accept(gettypedeps)
 
     usingTypedefs = gettypedeps.usingTypedefs
@@ -2388,16 +2420,30 @@ def _generateCxxStruct(sd):
         getconst = MethodDefn(getconstdecl)
         getconst.addstmt(StmtReturn(f.constRefExpr()))
 
         struct.addstmts([get, getconst, Whitespace.NL])
 
     # private:
     struct.addstmt(Label.PRIVATE)
 
+    # Static assertions to ensure our assumptions about field layout match
+    # what the compiler is actually producing.  We define this as a member
+    # function, rather than throwing the assertions in the constructor or
+    # similar, because we don't want to evaluate the static assertions every
+    # time the header file containing the structure is included.
+    staticasserts = _fieldStaticAssertions(sd)
+    if staticasserts:
+        method = MethodDefn(MethodDecl('StaticAssertions',
+                                       params=[],
+                                       ret=Type.VOID,
+                                       const=True))
+        method.addstmts(staticasserts)
+        struct.addstmts([method])
+
     # members
     struct.addstmts([StmtDecl(Decl(f.bareType(), f.memberVar().name))
                      for f in sd.fields_member_order()])
 
     return forwarddeclstmts, fulldecltypes, struct
 
 # --------------------------------------------------