qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi: Track owner of each object member

Future commits will migrate semantic checking away from parsing
and over to the various QAPISchema*.check() methods. But to
report an error message about an incorrect semantic use of a
member of an object type, it helps to know which type, command,
or event owns the member. In particular, when a member is
inherited from a base type, it is desirable to associate the
member name with the base type (and not the type calling
member.check()).

Rather than packing additional information into the seen array
passed to each member.check() (as in seen[m.name] = {'member':m,
'owner':type}), it is easier to have each member track the name
of the owner type in the first place (keeping things simpler
with the existing seen[m.name] = m). The new member.owner field
is set via a new set_owner() method, called when registering
the members and variants arrays with an object or variant type.
Track only a name, and not the actual type object, to avoid
creating a circular python reference chain.

Note that Variants.set_owner() method does not set the owner
for the tag_member field; this field is set earlier either as
part of an object's non-variant members, or explicitly by
alternates.

The source information is intended for human consumption in
error messages, and a new describe() method is added to access
the resulting information. For example, given the qapi:
{ 'command': 'foo', 'data': { 'string': 'str' } }
an implementation of visit_command() that calls
arg_type.members[0].describe()
will see "'string' (parameter of foo)".

To make the human-readable name of implicit types work without
duplicating efforts, the describe() method has to reverse the
name of implicit types, via the helper _pretty_owner().

No change to generated code.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1447836791-369-16-git-send-email-eblake@redhat.com>
[Incorrect & unused -wrapper case in _pretty_owner() dropped]
Signed-off-by: Markus Armbruster <armbru@redhat.com>

authored by

Eric Blake and committed by
Markus Armbruster
88d4ef8b 61a94661

+38 -2
+38 -2
scripts/qapi.py
··· 957 957 assert base is None or isinstance(base, str) 958 958 for m in local_members: 959 959 assert isinstance(m, QAPISchemaObjectTypeMember) 960 - assert (variants is None or 961 - isinstance(variants, QAPISchemaObjectTypeVariants)) 960 + m.set_owner(name) 961 + if variants is not None: 962 + assert isinstance(variants, QAPISchemaObjectTypeVariants) 963 + variants.set_owner(name) 962 964 self._base_name = base 963 965 self.base = None 964 966 self.local_members = local_members ··· 1013 1015 1014 1016 1015 1017 class QAPISchemaObjectTypeMember(object): 1018 + role = 'member' 1019 + 1016 1020 def __init__(self, name, typ, optional): 1017 1021 assert isinstance(name, str) 1018 1022 assert isinstance(typ, str) ··· 1021 1025 self._type_name = typ 1022 1026 self.type = None 1023 1027 self.optional = optional 1028 + self.owner = None 1029 + 1030 + def set_owner(self, name): 1031 + assert not self.owner 1032 + self.owner = name 1024 1033 1025 1034 def check(self, schema): 1035 + assert self.owner 1026 1036 self.type = schema.lookup_type(self._type_name) 1027 1037 assert self.type 1028 1038 ··· 1031 1041 assert self.name not in seen 1032 1042 seen[self.name] = self 1033 1043 1044 + def _pretty_owner(self): 1045 + owner = self.owner 1046 + if owner.startswith(':obj-'): 1047 + # See QAPISchema._make_implicit_object_type() - reverse the 1048 + # mapping there to create a nice human-readable description 1049 + owner = owner[5:] 1050 + if owner.endswith('-arg'): 1051 + return '(parameter of %s)' % owner[:-4] 1052 + else: 1053 + assert owner.endswith('-wrapper') 1054 + # Unreachable and not implemented 1055 + assert False 1056 + return '(%s of %s)' % (self.role, owner) 1057 + 1058 + def describe(self): 1059 + return "'%s' %s" % (self.name, self._pretty_owner()) 1060 + 1034 1061 1035 1062 class QAPISchemaObjectTypeVariants(object): 1036 1063 def __init__(self, tag_name, tag_member, variants): ··· 1046 1073 self.tag_name = tag_name 1047 1074 self.tag_member = tag_member 1048 1075 self.variants = variants 1076 + 1077 + def set_owner(self, name): 1078 + for v in self.variants: 1079 + v.set_owner(name) 1049 1080 1050 1081 def check(self, schema, seen): 1051 1082 if not self.tag_member: # flat union ··· 1066 1097 1067 1098 1068 1099 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1100 + role = 'branch' 1101 + 1069 1102 def __init__(self, name, typ): 1070 1103 QAPISchemaObjectTypeMember.__init__(self, name, typ, False) 1071 1104 ··· 1085 1118 QAPISchemaType.__init__(self, name, info) 1086 1119 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1087 1120 assert not variants.tag_name 1121 + variants.set_owner(name) 1122 + variants.tag_member.set_owner(self.name) 1088 1123 self.variants = variants 1089 1124 1090 1125 def check(self, schema): ··· 1217 1252 def _make_implicit_object_type(self, name, info, role, members): 1218 1253 if not members: 1219 1254 return None 1255 + # See also QAPISchemaObjectTypeMember._pretty_owner() 1220 1256 name = ':obj-%s-%s' % (name, role) 1221 1257 if not self.lookup_entity(name, QAPISchemaObjectType): 1222 1258 self._def_entity(QAPISchemaObjectType(name, info, None,