spice-common/python_modules/ptypes.py
Frediano Ziglio d8fe0cbb84 codegen: Remove bytes array length support
This syntax was only used in protocol 1 which has been removed
time ago.
Beside not being used it's confusing and prone to errors,
array size is specified using 2 identifiers, one reporting
bytes and the other number of items, one used for marshalling,
the other for demarshalling.

Signed-off-by: Frediano Ziglio <freddy77@gmail.com>
2020-10-06 13:09:56 +01:00

1169 lines
36 KiB
Python

from . import codegen
import types
_types_by_name = {}
_types = []
def type_exists(name):
return name in _types_by_name
def lookup_type(name):
return _types_by_name[name]
def get_named_types():
return _types
# Some attribute are propagated from member to the type as they really
# are part of the type definition, rather than the member. This applies
# only to attributes that affect pointer or array attributes, as these
# are member local types, unlike e.g. a Struct that may be used by
# other members
propagated_attributes=["ptr_array", "nonnull", "chunk"]
valid_attributes_generic=set([
# embedded/appended at the end of the resulting C structure
'end',
# the C structure contains a pointer to data
# for instance we want to write an array to an allocated array
# The demarshaller allocates space for data that this pointer points to
'to_ptr',
# write output to this C structure
'ctype',
# prefix for flags/values enumerations
'prefix',
# used in demarshaller to use directly data from message without a copy
'nocopy',
# store member array in a pointer
# Has an argument which is the name of a C field which will store the
# array length
# The demarshaller stores a reference to the original message buffer so
# you should keep a reference to the original message to avoid a dangling
# pointer
# Is useful for large buffers to avoid extra memory allocation and copying
'as_ptr',
# do not generate marshall code
# used for last members to be able to marshall them manually
'nomarshal',
# ??? not used by python code
'zero_terminated',
# force generating marshaller code, applies to pointers which by
# default are not marshalled (submarshallers are generated)
'marshall',
# this pointer member cannot be null
'nonnull',
# this flag member contains only a single flag
'unique_flag',
# represent array as an array of pointers (in the C structure)
# for instance a "Type name[size] @ptr_array" in the protocol
# will be stored in a "Type *name[0]" field in the C structure
'ptr_array',
'outvar',
# C structure has an anonymous member (used in switch)
'anon',
# the C structure contains a pointer to a SpiceChunks structure.
# The SpiceChunks structure is allocated inside the demarshalled
# buffer and data will point to original message.
'chunk',
# this channel is contained in an #ifdef section
# the argument specifies the preprocessor define to check
'ifdef',
# write this member as zero on network
# when marshalling, a zero field is written to the network
# when demarshalling, the field is read from the network and discarded
'zero',
# this attribute does not exist on the network, fill just structure with the value
'virtual',
# generate C structure declarations from protocol definition
'declare',
])
attributes_with_arguments=set([
'ctype',
'prefix',
'as_ptr',
'outvar',
'ifdef',
'virtual',
])
def fix_attributes(attribute_list, valid_attributes=valid_attributes_generic):
attrs = {}
for attr in attribute_list:
name = attr[0][1:] # [1:] strips the leading '@' from the name
lst = attr[1:]
if not name in valid_attributes:
raise Exception("Attribute %s not recognized" % name)
if not name in attributes_with_arguments:
if len(lst) > 0:
raise Exception("Attribute %s specified with options" % name)
elif len(lst) > 1:
raise Exception("Attribute %s has more than 1 argument" % name)
attrs[name] = lst
# these attributes specify output format, only one can be set
output_attrs = set(['end', 'to_ptr', 'as_ptr', 'ptr_array', 'zero', 'chunk'])
if len(output_attrs.intersection(attrs.keys())) > 1:
raise Exception("Multiple output type attributes specified %s" % output_attrs)
return attrs
class Type:
def __init__(self):
self.attributes = {}
self.registered = False
self.name = None
def has_name(self):
return self.name != None
def is_primitive(self):
return False
def is_fixed_sizeof(self):
return True
def is_extra_size(self):
return False
def contains_extra_size(self):
return False
def is_fixed_nw_size(self):
return True
def is_array(self):
return isinstance(self, ArrayType)
def contains_member(self, member):
return False
def is_struct(self):
return isinstance(self, StructType)
def is_pointer(self):
return isinstance(self, PointerType)
def get_num_pointers(self):
return 0
def get_pointer_names(self, marshalled):
return []
def sizeof(self):
return "sizeof(%s)" % (self.c_type())
def __repr__(self):
return self.__str__()
def __str__(self):
if self.name != None:
return self.name
return "anonymous type"
def resolve(self):
return self
def register(self):
if self.registered or self.name == None:
return
self.registered = True
if self.name in _types_by_name:
raise Exception("Type %s already defined" % self.name)
_types.append(self)
_types_by_name[self.name] = self
def has_attr(self, name):
return name in self.attributes
class TypeRef(Type):
def __init__(self, name):
Type.__init__(self)
self.name = name
def __str__(self):
return "ref to %s" % (self.name)
def resolve(self):
if self.name not in _types_by_name:
raise Exception("Unknown type %s" % self.name)
return _types_by_name[self.name]
def register(self):
assert False, "Can't register TypeRef!"
class IntegerType(Type):
def __init__(self, bits, signed):
Type.__init__(self)
self.bits = bits
self.signed = signed
if signed:
self.name = "int%d" % bits
else:
self.name = "uint%d" % bits
def primitive_type(self):
return self.name
def c_type(self):
return self.name + "_t"
def get_fixed_nw_size(self):
return self.bits // 8
def is_primitive(self):
return True
class TypeAlias(Type):
def __init__(self, name, the_type, attribute_list):
Type.__init__(self)
self.name = name
self.the_type = the_type
self.attributes = fix_attributes(attribute_list)
def primitive_type(self):
return self.the_type.primitive_type()
def resolve(self):
self.the_type = self.the_type.resolve()
return self
def __str__(self):
return "alias %s" % self.name
def is_primitive(self):
return self.the_type.is_primitive()
def is_fixed_sizeof(self):
return self.the_type.is_fixed_sizeof()
def is_fixed_nw_size(self):
return self.the_type.is_fixed_nw_size()
def get_fixed_nw_size(self):
return self.the_type.get_fixed_nw_size()
def get_num_pointers(self):
return self.the_type.get_num_pointers()
def get_pointer_names(self, marshalled):
return self.the_type.get_pointer_names(marshalled)
def c_type(self):
if self.has_attr("ctype"):
return self.attributes["ctype"][0]
return self.the_type.c_type()
class EnumBaseType(Type):
def is_enum(self):
return isinstance(self, EnumType)
def primitive_type(self):
return "uint%d" % (self.bits)
def c_type(self):
return "uint%d_t" % (self.bits)
def c_name(self):
return codegen.prefix_camel(self.name)
def c_enumname(self, value):
return self.c_enumname_by_name(self.names[value])
def c_enumname_by_name(self, name):
if self.has_attr("prefix"):
return self.attributes["prefix"][0] + name
return codegen.prefix_underscore_upper(self.name.upper(), name)
def is_primitive(self):
return True
def get_fixed_nw_size(self):
return self.bits // 8
# generates a value-name table suitable for use with the wireshark protocol
# dissector
def c_describe(self, writer):
writer.write("static const value_string %s_vs[] = " % codegen.prefix_underscore_lower(self.name))
writer.begin_block()
values = list(self.names.keys())
values.sort()
for i in values:
writer.write("{ ")
writer.write(self.c_enumname(i))
writer.write(", \"%s\" }," % self.names[i])
writer.newline()
writer.write("{ 0, NULL }")
writer.end_block(semicolon=True)
writer.newline()
class EnumType(EnumBaseType):
def __init__(self, bits, name, enums, attribute_list):
Type.__init__(self)
self.bits = bits
self.name = name
last = -1
names = {}
values = {}
attributes = {}
for v in enums:
name = v[0]
if v[1] is not None:
value = v[1]
else:
value = last + 1
last = value
assert value not in names
names[value] = name
values[name] = value
attributes[value] = fix_attributes(v[2], set(['deprecated']))
self.names = names
self.values = values
self.val_attributes = attributes
self.attributes = fix_attributes(attribute_list)
def __str__(self):
return "enum %s" % self.name
def c_define(self, writer):
writer.write("typedef enum ")
writer.write(self.c_name())
writer.begin_block()
values = list(self.names.keys())
values.sort()
current_default = 0
for i in values:
writer.write(self.c_enumname(i))
if i != current_default:
writer.write(" = %d" % (i))
if 'deprecated' in self.val_attributes[i]:
writer.write(' SPICE_GNUC_DEPRECATED')
writer.write(",")
writer.newline()
current_default = i + 1
writer.newline()
writer.write(codegen.prefix_underscore_upper(self.name.upper(), "ENUM_END"))
writer.newline()
writer.end_block(newline=False)
writer.write(" ")
writer.write(self.c_name())
writer.write(";")
writer.newline()
writer.newline()
class FlagsType(EnumBaseType):
def __init__(self, bits, name, flags, attribute_list):
Type.__init__(self)
self.bits = bits
self.name = name
last = -1
names = {}
values = {}
attributes = {}
for v in flags:
name = v[0]
if v[1] is not None:
value = v[1]
else:
value = last + 1
last = value
assert value not in names
names[value] = name
values[name] = value
attributes[value] = fix_attributes(v[2], set(['deprecated']))
self.names = names
self.values = values
self.val_attributes = attributes
self.attributes = fix_attributes(attribute_list)
def __str__(self):
return "flags %s" % self.name
def c_define(self, writer):
writer.write("typedef enum ")
writer.write(self.c_name())
writer.begin_block()
values = list(self.names.keys())
values.sort()
mask = 0
for i in values:
writer.write(self.c_enumname(i))
mask = mask | (1<<i)
writer.write(" = (1 << %d)" % (i))
if 'deprecated' in self.val_attributes[i]:
writer.write(' SPICE_GNUC_DEPRECATED')
writer.write(",")
writer.newline()
current_default = i + 1
writer.newline()
writer.write(codegen.prefix_underscore_upper(self.name.upper(), "MASK"))
writer.write(" = 0x%x" % (mask))
writer.newline()
writer.end_block(newline=False)
writer.write(" ")
writer.write(self.c_name())
writer.write(";")
writer.newline()
writer.newline()
class ArrayType(Type):
def __init__(self, element_type, size):
Type.__init__(self)
self.name = None
self.element_type = element_type
self.size = size
def __str__(self):
if self.size == None:
return "%s[]" % (str(self.element_type))
else:
return "%s[%s]" % (str(self.element_type), str(self.size))
def resolve(self):
self.element_type = self.element_type.resolve()
return self
def is_constant_length(self):
return isinstance(self.size, int)
def is_remaining_length(self):
return isinstance(self.size, str) and len(self.size) == 0
def is_identifier_length(self):
return isinstance(self.size, str) and len(self.size) > 0
def is_image_size_length(self):
if isinstance(self.size, int) or isinstance(self.size, str):
return False
return self.size[0] == "image_size"
def is_cstring_length(self):
if isinstance(self.size, int) or isinstance(self.size, str):
return False
return self.size[0] == "cstring"
def is_fixed_sizeof(self):
return self.is_constant_length() and self.element_type.is_fixed_sizeof()
def is_fixed_nw_size(self):
return self.is_constant_length() and self.element_type.is_fixed_nw_size()
def get_fixed_nw_size(self):
if not self.is_fixed_nw_size():
raise Exception("Not a fixed size type")
return self.element_type.get_fixed_nw_size() * self.size
def get_num_pointers(self):
element_count = self.element_type.get_num_pointers()
if element_count == 0:
return 0
if self.is_constant_length():
return element_count * self.size
raise Exception("Pointers in dynamic arrays not supported")
def get_pointer_names(self, marshalled):
element_count = self.element_type.get_num_pointers()
if element_count == 0:
return []
raise Exception("Pointer names in arrays not supported")
def is_extra_size(self):
return self.has_attr("ptr_array")
def contains_extra_size(self):
return self.element_type.contains_extra_size() or self.has_attr("chunk")
def sizeof(self):
return "%s * %s" % (self.element_type.sizeof(), self.size)
def c_type(self):
return self.element_type.c_type()
def check_valid(self, member):
# These attribute corresponds to specific structure size
if member.has_attr("chunk") or member.has_attr("as_ptr"):
return
# These attribute indicate that the array is stored in the structure
# as a pointer of the array. If there's no way to retrieve the length
# of the array give error, as the user has no way to do bound checks
if member.has_attr("to_ptr") or member.has_attr("ptr_array"):
if not (self.is_identifier_length() or self.is_constant_length()):
raise Exception("Unsecure, no length of array")
return
# This attribute indicate that the array is store at the end
# of the structure, the user will compute the length from the
# entire message size
if member.has_end_attr():
return
# Avoid bug, the array has no length specified and no space
# would be allocated
if self.is_remaining_length():
raise Exception('C output array is not allocated')
# For constant length (like "foo[5]") the field is a sized array
# For identifier automatically a pointer to allocated data is store,
# in this case user can read the size using the other field specified
# by the identifier
if self.is_constant_length() or self.is_identifier_length():
return
raise NotImplementedError('unknown array %s' % str(self))
def generate_c_declaration(self, writer, member):
self.check_valid(member)
name = member.name
if member.has_attr("chunk"):
return writer.writeln('SpiceChunks *%s;' % name)
if member.has_attr("as_ptr"):
len_var = member.attributes["as_ptr"][0]
writer.writeln('uint32_t %s;' % len_var)
return writer.writeln('%s *%s;' % (self.c_type(), name))
if member.has_attr("to_ptr"):
return writer.writeln('%s *%s;' % (self.c_type(), name))
if member.has_attr("ptr_array"):
return writer.writeln('%s *%s[0];' % (self.c_type(), name))
if member.has_end_attr():
return writer.writeln('%s %s[0];' % (self.c_type(), name))
if self.is_constant_length():
return writer.writeln('%s %s[%s];' % (self.c_type(), name, self.size))
if self.is_identifier_length():
return writer.writeln('%s *%s;' % (self.c_type(), name))
raise NotImplementedError('unknown array %s' % str(self))
class PointerType(Type):
def __init__(self, target_type):
Type.__init__(self)
self.name = None
self.target_type = target_type
def __str__(self):
return "%s*" % (str(self.target_type))
def resolve(self):
self.target_type = self.target_type.resolve()
return self
def is_fixed_nw_size(self):
return True
def is_primitive(self):
return True
def primitive_type(self):
return "uint32"
def get_fixed_nw_size(self):
return 4
def c_type(self):
return "uint32_t"
def contains_extra_size(self):
return True
def get_num_pointers(self):
return 1
def generate_c_declaration(self, writer, member):
target_type = self.target_type
is_array = target_type.is_array()
if not is_array or target_type.is_identifier_length():
writer.writeln("%s *%s;" % (target_type.c_type(), member.name))
return
raise NotImplementedError('Some pointers to array declarations are not implemented %s' %
member)
class Containee:
def __init__(self):
self.attributes = {}
def is_switch(self):
return False
def is_pointer(self):
return not self.is_switch() and self.member_type.is_pointer()
def is_array(self):
return not self.is_switch() and self.member_type.is_array()
def is_struct(self):
return not self.is_switch() and self.member_type.is_struct()
def is_primitive(self):
return not self.is_switch() and self.member_type.is_primitive()
def has_attr(self, name):
return name in self.attributes
def has_end_attr(self):
return self.has_attr("end")
class Member(Containee):
def __init__(self, name, member_type, attribute_list):
Containee.__init__(self)
self.name = name
self.member_type = member_type
self.attributes = fix_attributes(attribute_list)
def resolve(self, container):
self.container = container
self.member_type = self.member_type.resolve()
self.member_type.register()
for i in propagated_attributes:
if self.has_attr(i):
self.member_type.attributes[i] = self.attributes[i]
return self
def contains_member(self, member):
return self.member_type.contains_member(member)
def is_primitive(self):
return self.member_type.is_primitive()
def is_fixed_sizeof(self):
if self.has_end_attr():
return False
return self.member_type.is_fixed_sizeof()
def is_extra_size(self):
return self.has_end_attr() or self.has_attr("to_ptr") or self.member_type.is_extra_size()
def is_fixed_nw_size(self):
if self.has_attr("virtual"):
return True
return self.member_type.is_fixed_nw_size()
def get_fixed_nw_size(self):
if self.has_attr("virtual"):
return 0
size = self.member_type.get_fixed_nw_size()
return size
def contains_extra_size(self):
return self.member_type.contains_extra_size()
def sizeof(self):
return self.member_type.sizeof()
def __repr__(self):
return "%s (%s)" % (str(self.name), str(self.member_type))
def get_num_pointers(self):
if self.has_attr("to_ptr"):
return 1
return self.member_type.get_num_pointers()
def get_pointer_names(self, marshalled):
if self.member_type.is_pointer():
if self.has_attr("marshall") == marshalled:
names = [self.name]
else:
names = []
else:
names = self.member_type.get_pointer_names(marshalled)
if self.has_attr("outvar"):
prefix = self.attributes["outvar"][0]
names = [prefix + "_" + name for name in names]
return names
def generate_c_declaration(self, writer):
if self.has_attr("zero"):
return
if self.is_pointer() or self.is_array():
self.member_type.generate_c_declaration(writer, self)
return
return writer.writeln("%s %s;" % (self.member_type.c_type(), self.name))
class SwitchCase:
def __init__(self, values, member):
self.values = values
self.member = member
self.members = [member]
def get_check(self, var_cname, var_type):
checks = []
for v in self.values:
if v == None:
return "1"
elif var_type.is_enum():
assert v[0] == "", "Negation of enumeration in switch is not supported"
checks.append("%s == %s" % (var_cname, var_type.c_enumname_by_name(v[1])))
else:
checks.append("%s(%s & %s)" % (v[0], var_cname, var_type.c_enumname_by_name(v[1])))
return " || ".join(checks)
def resolve(self, container):
self.switch = container
self.member = self.member.resolve(self)
return self
def get_num_pointers(self):
return self.member.get_num_pointers()
def get_pointer_names(self, marshalled):
return self.member.get_pointer_names(marshalled)
class Switch(Containee):
def __init__(self, variable, cases, name, attribute_list):
Containee.__init__(self)
self.variable = variable
self.name = name
self.cases = cases
self.attributes = fix_attributes(attribute_list)
def is_switch(self):
return True
def lookup_case_member(self, name):
for c in self.cases:
if c.member.name == name:
return c.member
return None
def has_switch_member(self, member):
for c in self.cases:
if c.member == member:
return True
return False
def resolve(self, container):
self.container = container
self.cases = [c.resolve(self) for c in self.cases]
return self
def __repr__(self):
return "switch on %s %s" % (str(self.variable),str(self.name))
def is_fixed_sizeof(self):
# Kinda weird, but we're unlikely to have a real struct if there is an @end
if self.has_end_attr():
return False
return True
def is_fixed_nw_size(self):
size = None
has_default = False
for c in self.cases:
for v in c.values:
if v == None:
has_default = True
if not c.member.is_fixed_nw_size():
return False
if size == None:
size = c.member.get_fixed_nw_size()
elif size != c.member.get_fixed_nw_size():
return False
# Fixed size if all elements listed, or has default
if has_default:
return True
key = self.container.lookup_member(self.variable)
return len(self.cases) == len(key.member_type.values)
def is_extra_size(self):
return self.has_end_attr()
def contains_extra_size(self):
for c in self.cases:
if c.member.is_extra_size():
return True
if c.member.contains_extra_size():
return True
return False
def get_fixed_nw_size(self):
if not self.is_fixed_nw_size():
raise Exception("Not a fixed size type")
size = 0
for c in self.cases:
size = max(size, c.member.get_fixed_nw_size())
return size
def sizeof(self):
return "sizeof(((%s *)NULL)->%s)" % (self.container.c_type(),
self.name)
def contains_member(self, member):
return False # TODO: Don't support switch deep member lookup yet
def get_num_pointers(self):
count = 0
for c in self.cases:
count = max(count, c.get_num_pointers())
return count
def get_pointer_names(self, marshalled):
names = []
for c in self.cases:
names = names + c.get_pointer_names(marshalled)
return names
def generate_c_declaration(self, writer):
if self.has_attr("anon") and len(self.cases) == 1:
self.cases[0].member.generate_c_declaration(writer)
return
writer.writeln('union {')
writer.indent()
for m in self.cases:
m.member.generate_c_declaration(writer)
writer.unindent()
writer.writeln('} %s;' % self.name)
class ContainerType(Type):
def is_fixed_sizeof(self):
for m in self.members:
if not m.is_fixed_sizeof():
return False
return True
def contains_extra_size(self):
for m in self.members:
if m.is_extra_size():
return True
if m.contains_extra_size():
return True
return False
def is_fixed_nw_size(self):
for i in self.members:
if not i.is_fixed_nw_size():
return False
return True
def get_fixed_nw_size(self):
size = 0
for i in self.members:
size = size + i.get_fixed_nw_size()
return size
def contains_member(self, member):
for m in self.members:
if m == member or m.contains_member(member):
return True
return False
def get_fixed_nw_offset(self, member):
size = 0
for i in self.members:
if i == member:
break
if i.contains_member(member):
size = size + i.member_type.get_fixed_nw_offset(member)
break
if i.is_fixed_nw_size():
size = size + i.get_fixed_nw_size()
return size
def resolve(self):
self.members = [m.resolve(self) for m in self.members]
return self
def get_num_pointers(self):
count = 0
for m in self.members:
count = count + m.get_num_pointers()
return count
def get_pointer_names(self, marshalled):
names = []
for m in self.members:
names = names + m.get_pointer_names(marshalled)
return names
def get_nw_offset(self, member, prefix = "", postfix = ""):
fixed = self.get_fixed_nw_offset(member)
v = []
container = self
while container != None:
members = container.members
container = None
for m in members:
if m == member:
break
if m.contains_member(member):
container = m.member_type
break
if m.is_switch() and m.has_switch_member(member):
break
if not m.is_fixed_nw_size():
v.append(prefix + m.name + postfix)
if len(v) > 0:
return str(fixed) + " + " + (" + ".join(v))
else:
return str(fixed)
def lookup_member(self, name):
dot = name.find('.')
rest = None
if dot >= 0:
rest = name[dot+1:]
name = name[:dot]
member = None
if name in self.members_by_name:
member = self.members_by_name[name]
else:
for m in self.members:
if m.is_switch():
member = m.lookup_case_member(name)
if member != None:
break
if member == None:
raise Exception("No member called %s found" % name)
if rest != None:
return member.member_type.lookup_member(rest)
return member
def generate_c_declaration(self, writer):
if not self.has_attr('declare'):
return
name = self.c_type()
writer.writeln('typedef struct %s {' % name)
writer.indent()
for m in self.members:
m.generate_c_declaration(writer)
if len(self.members) == 0:
# make sure generated structure are not empty
writer.writeln("char dummy[0];")
writer.unindent()
writer.writeln('} %s;' % name).newline()
class StructType(ContainerType):
def __init__(self, name, members, attribute_list):
Type.__init__(self)
self.name = name
self.members = members
self.members_by_name = {}
for m in members:
self.members_by_name[m.name] = m
self.attributes = fix_attributes(attribute_list)
def __str__(self):
if self.name == None:
return "anonymous struct"
else:
return "struct %s" % self.name
def c_type(self):
if self.has_attr("ctype"):
return self.attributes["ctype"][0]
return codegen.prefix_camel(self.name)
class MessageType(ContainerType):
def __init__(self, name, members, attribute_list):
Type.__init__(self)
self.name = name
self.members = members
self.members_by_name = {}
for m in members:
self.members_by_name[m.name] = m
self.reverse_members = {} # ChannelMembers referencing this message
self.attributes = fix_attributes(attribute_list)
def __str__(self):
if self.name == None:
return "anonymous message"
else:
return "message %s" % self.name
def c_name(self):
if self.name == None:
cms = list(self.reverse_members.keys())
if len(cms) != 1:
raise "Unknown typename for message"
cm = cms[0]
channelname = cm.channel.member_name
if channelname == None:
channelname = ""
else:
channelname = channelname + "_"
if cm.is_server:
return "msg_" + channelname + cm.name
else:
return "msgc_" + channelname + cm.name
else:
return codegen.prefix_camel("Msg", self.name)
def c_type(self):
if self.has_attr("ctype"):
return self.attributes["ctype"][0]
if self.name == None:
cms = list(self.reverse_members.keys())
if len(cms) != 1:
raise "Unknown typename for message"
cm = cms[0]
channelname = cm.channel.member_name
if channelname == None:
channelname = ""
if cm.is_server:
return codegen.prefix_camel("Msg", channelname, cm.name)
return codegen.prefix_camel("Msgc", channelname, cm.name)
else:
return codegen.prefix_camel("Msg", self.name)
class ChannelMember(Containee):
def __init__(self, name, message_type, value):
Containee.__init__(self)
self.name = name
self.message_type = message_type
self.value = value
def resolve(self, channel):
self.channel = channel
self.message_type = self.message_type.resolve()
self.message_type.reverse_members[self] = 1
return self
def __repr__(self):
return "%s (%s)" % (str(self.name), str(self.message_type))
class ChannelType(Type):
def __init__(self, name, base, members, attribute_list):
Type.__init__(self)
self.name = name
self.base = base
self.member_name = None
self.members = members
self.attributes = fix_attributes(attribute_list)
def __str__(self):
if self.name == None:
return "anonymous channel"
else:
return "channel %s" % self.name
def is_fixed_nw_size(self):
return False
def get_client_message(self, name):
return self.client_messages_byname[name]
def get_server_message(self, name):
return self.server_messages_byname[name]
def resolve(self):
class MessagesInfo:
def __init__(self, is_server, messages=[], messages_byname={}):
self.is_server = is_server
self.messages = messages[:]
self.messages_byname = messages_byname.copy()
self.count = 1
self.messages_byvalue = {}
for m in self.messages:
self.messages_byvalue[m.value] = m
if self.base is None:
server_info = MessagesInfo(True)
client_info = MessagesInfo(False)
else:
self.base = self.base.resolve()
server_info = MessagesInfo(True, self.base.server_messages,
self.base.server_messages_byname)
client_info = MessagesInfo(False, self.base.client_messages,
self.base.client_messages_byname)
# Set default member_name, FooChannel -> foo
self.member_name = self.name[:-7].lower()
info = server_info
for m in self.members:
if m == "server":
info = server_info
elif m == "client":
info = client_info
else:
m.is_server = info.is_server
m = m.resolve(self)
if not m.value:
m.value = info.count
info.count = m.value + 1
info.messages.append(m)
if m.name in info.messages_byname:
raise Exception("Duplicated message name '%s' in channel '%s'" % (m.name, self.name))
info.messages_byname[m.name] = m
if m.value in info.messages_byvalue:
raise Exception("Duplicated message value %d between '%s' and '%s' in channel '%s'" % (
m.value, info.messages_byvalue[m.value].name, m.name, self.name))
info.messages_byvalue[m.value] = m
self.server_messages = server_info.messages
self.server_messages_byname = server_info.messages_byname
self.client_messages = client_info.messages
self.client_messages_byname = client_info.messages_byname
return self
class ProtocolMember:
def __init__(self, name, channel_type, value):
self.name = name
self.channel_type = channel_type
self.value = value
def resolve(self, protocol):
self.channel_type = self.channel_type.resolve()
self.channel_type.member_name = self.name
return self
def __repr__(self):
return "%s (%s)" % (str(self.name), str(self.channel_type))
class ProtocolType(Type):
def __init__(self, name, channels):
Type.__init__(self)
self.name = name
self.channels = channels
def __str__(self):
if self.name == None:
return "anonymous protocol"
else:
return "protocol %s" % self.name
def is_fixed_nw_size(self):
return False
def resolve(self):
count = 1
for m in self.channels:
m = m.resolve(self)
if m.value:
count = m.value + 1
else:
m.value = count
count = count + 1
return self
class FdType(IntegerType):
def __init__(self):
IntegerType.__init__(self, 0, False)
self.name = "fd"
def c_type(self):
return "int"
int8 = IntegerType(8, True)
uint8 = IntegerType(8, False)
int16 = IntegerType(16, True)
uint16 = IntegerType(16, False)
int32 = IntegerType(32, True)
uint32 = IntegerType(32, False)
int64 = IntegerType(64, True)
uint64 = IntegerType(64, False)
unix_fd = FdType()