blob: b4aa9d94916f50a2a76fba4e3905648029e99691 [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This module's classes provide an interface to mojo modules. Modules are
# collections of interfaces and structs to be used by mojo ipc clients and
# servers.
#
# A simple interface would be created this way:
# module = mojom.generate.module.Module('Foo')
# interface = module.AddInterface('Bar')
# method = interface.AddMethod('Tat', 0)
# method.AddParameter('baz', 0, mojom.INT32)
class Kind(object):
def __init__(self, spec=None):
self.spec = spec
self.parent_kind = None
class ReferenceKind(Kind):
"""ReferenceKind represents pointer types and handle types.
A type is nullable if null (for pointer types) or invalid handle (for handle
types) is a legal value for the type.
"""
def __init__(self, spec=None, is_nullable=False):
assert spec is None or is_nullable == spec.startswith('?')
Kind.__init__(self, spec)
self.is_nullable = is_nullable
self.shared_definition = {}
def MakeNullableKind(self):
assert not self.is_nullable
if self == STRING:
return NULLABLE_STRING
if self == HANDLE:
return NULLABLE_HANDLE
if self == DCPIPE:
return NULLABLE_DCPIPE
if self == DPPIPE:
return NULLABLE_DPPIPE
if self == MSGPIPE:
return NULLABLE_MSGPIPE
if self == SHAREDBUFFER:
return NULLABLE_SHAREDBUFFER
nullable_kind = type(self)()
nullable_kind.shared_definition = self.shared_definition
if self.spec is not None:
nullable_kind.spec = '?' + self.spec
nullable_kind.is_nullable = True
return nullable_kind
@classmethod
def AddSharedProperty(cls, name):
"""Adds a property |name| to |cls|, which accesses the corresponding item in
|shared_definition|.
The reason of adding such indirection is to enable sharing definition
between a reference kind and its nullable variation. For example:
a = Struct('test_struct_1')
b = a.MakeNullableKind()
a.name = 'test_struct_2'
print b.name # Outputs 'test_struct_2'.
"""
def Get(self):
return self.shared_definition[name]
def Set(self, value):
self.shared_definition[name] = value
setattr(cls, name, property(Get, Set))
# Initialize the set of primitive types. These can be accessed by clients.
BOOL = Kind('b')
INT8 = Kind('i8')
INT16 = Kind('i16')
INT32 = Kind('i32')
INT64 = Kind('i64')
UINT8 = Kind('u8')
UINT16 = Kind('u16')
UINT32 = Kind('u32')
UINT64 = Kind('u64')
FLOAT = Kind('f')
DOUBLE = Kind('d')
STRING = ReferenceKind('s')
HANDLE = ReferenceKind('h')
DCPIPE = ReferenceKind('h:d:c')
DPPIPE = ReferenceKind('h:d:p')
MSGPIPE = ReferenceKind('h:m')
SHAREDBUFFER = ReferenceKind('h:s')
NULLABLE_STRING = ReferenceKind('?s', True)
NULLABLE_HANDLE = ReferenceKind('?h', True)
NULLABLE_DCPIPE = ReferenceKind('?h:d:c', True)
NULLABLE_DPPIPE = ReferenceKind('?h:d:p', True)
NULLABLE_MSGPIPE = ReferenceKind('?h:m', True)
NULLABLE_SHAREDBUFFER = ReferenceKind('?h:s', True)
# Collection of all Primitive types
PRIMITIVES = (
BOOL,
INT8,
INT16,
INT32,
INT64,
UINT8,
UINT16,
UINT32,
UINT64,
FLOAT,
DOUBLE,
STRING,
HANDLE,
DCPIPE,
DPPIPE,
MSGPIPE,
SHAREDBUFFER,
NULLABLE_STRING,
NULLABLE_HANDLE,
NULLABLE_DCPIPE,
NULLABLE_DPPIPE,
NULLABLE_MSGPIPE,
NULLABLE_SHAREDBUFFER
)
class NamedValue(object):
def __init__(self, module=None, parent_kind=None, name=None):
self.module = module
if module:
self.namespace = module.namespace
self.parent_kind = parent_kind
self.name = name
self.imported_from = None
def GetSpec(self):
return (self.namespace + '.' +
(self.parent_kind and (self.parent_kind.name + '.') or "") +
self.name)
class BuiltinValue(object):
def __init__(self, value):
self.value = value
class ConstantValue(NamedValue):
def __init__(self, module=None, parent_kind=None, constant=None):
if constant:
NamedValue.__init__(self, module, parent_kind, constant.name)
else:
NamedValue.__init__(self, module, parent_kind)
self.constant = constant
class EnumValue(NamedValue):
def __init__(self, module=None, enum=None, field=None):
if enum and field:
NamedValue.__init__(self, module, enum.parent_kind, field.name)
else:
NamedValue.__init__(self, module)
self.enum = enum
def GetSpec(self):
return (self.namespace + '.' +
(self.parent_kind and (self.parent_kind.name + '.') or "") +
self.enum.name + '.' + self.name)
class Constant(object):
def __init__(self, name=None, kind=None, value=None, parent_kind=None):
self.name = name
self.kind = kind
self.value = value
self.parent_kind = parent_kind
class Field(object):
def __init__(self, name=None, kind=None, ordinal=None, default=None,
attributes=None):
if self.__class__.__name__ == 'Field':
raise Exception()
self.name = name
self.kind = kind
self.ordinal = ordinal
self.default = default
self.attributes = attributes
class StructField(Field): pass
class UnionField(Field): pass
class Struct(ReferenceKind):
ReferenceKind.AddSharedProperty('name')
ReferenceKind.AddSharedProperty('module')
ReferenceKind.AddSharedProperty('imported_from')
ReferenceKind.AddSharedProperty('fields')
ReferenceKind.AddSharedProperty('attributes')
ReferenceKind.AddSharedProperty('constants')
ReferenceKind.AddSharedProperty('enums')
ReferenceKind.AddSharedProperty('type_key')
def __init__(self, name=None, module=None, attributes=None):
if name is not None:
spec = 'x:' + name
else:
spec = None
ReferenceKind.__init__(self, spec)
self.name = name
self.module = module
self.imported_from = None
self.fields = []
self.constants = []
self.enums = []
self.attributes = attributes
def AddField(self, name, kind, ordinal=None, default=None, attributes=None):
field = StructField(name, kind, ordinal, default, attributes)
self.fields.append(field)
return field
class Union(ReferenceKind):
ReferenceKind.AddSharedProperty('name')
ReferenceKind.AddSharedProperty('module')
ReferenceKind.AddSharedProperty('imported_from')
ReferenceKind.AddSharedProperty('fields')
ReferenceKind.AddSharedProperty('attributes')
ReferenceKind.AddSharedProperty('type_key')
def __init__(self, name=None, module=None, attributes=None):
if name is not None:
spec = 'x:' + name
else:
spec = None
ReferenceKind.__init__(self, spec)
self.name = name
self.module = module
self.imported_from = None
self.fields = []
self.attributes = attributes
def AddField(self, name, kind, ordinal=None, attributes=None):
field = UnionField(name, kind, ordinal, None, attributes)
self.fields.append(field)
return field
class Array(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
ReferenceKind.AddSharedProperty('length')
def __init__(self, kind=None, length=None):
if kind is not None:
if length is not None:
spec = 'a%d:%s' % (length, kind.spec)
else:
spec = 'a:%s' % kind.spec
ReferenceKind.__init__(self, spec)
else:
ReferenceKind.__init__(self)
self.kind = kind
self.length = length
class Map(ReferenceKind):
ReferenceKind.AddSharedProperty('key_kind')
ReferenceKind.AddSharedProperty('value_kind')
def __init__(self, key_kind=None, value_kind=None):
if (key_kind is not None and value_kind is not None):
ReferenceKind.__init__(self,
'm[' + key_kind.spec + '][' + value_kind.spec +
']')
if IsNullableKind(key_kind):
raise Exception("Nullable kinds cannot be keys in maps.")
if IsStructKind(key_kind):
# TODO(erg): It would sometimes be nice if we could key on struct
# values. However, what happens if the struct has a handle in it? Or
# non-copyable data like an array?
raise Exception("Structs cannot be keys in maps.")
if IsAnyHandleKind(key_kind):
raise Exception("Handles cannot be keys in maps.")
if IsInterfaceKind(key_kind):
raise Exception("Interfaces cannot be keys in maps.")
if IsArrayKind(key_kind):
raise Exception("Arrays cannot be keys in maps.")
else:
ReferenceKind.__init__(self)
self.key_kind = key_kind
self.value_kind = value_kind
class InterfaceRequest(ReferenceKind):
ReferenceKind.AddSharedProperty('kind')
def __init__(self, kind=None):
if kind is not None:
if not isinstance(kind, Interface):
raise Exception(
"Interface request requires %r to be an interface." % kind.spec)
ReferenceKind.__init__(self, 'r:' + kind.spec)
else:
ReferenceKind.__init__(self)
self.kind = kind
class Parameter(object):
def __init__(self, name=None, kind=None, ordinal=None, default=None,
attributes=None):
self.name = name
self.ordinal = ordinal
self.kind = kind
self.default = default
self.attributes = attributes
class Method(object):
def __init__(self, interface, name, ordinal=None, attributes=None):
self.interface = interface
self.name = name
self.ordinal = ordinal
self.parameters = []
self.response_parameters = None
self.attributes = attributes
def AddParameter(self, name, kind, ordinal=None, default=None,
attributes=None):
parameter = Parameter(name, kind, ordinal, default, attributes)
self.parameters.append(parameter)
return parameter
def AddResponseParameter(self, name, kind, ordinal=None, default=None,
attributes=None):
if self.response_parameters == None:
self.response_parameters = []
parameter = Parameter(name, kind, ordinal, default, attributes)
self.response_parameters.append(parameter)
return parameter
class Interface(ReferenceKind):
ReferenceKind.AddSharedProperty('module')
ReferenceKind.AddSharedProperty('name')
ReferenceKind.AddSharedProperty('imported_from')
ReferenceKind.AddSharedProperty('methods')
ReferenceKind.AddSharedProperty('attributes')
ReferenceKind.AddSharedProperty('constants')
ReferenceKind.AddSharedProperty('enums')
ReferenceKind.AddSharedProperty('type_key')
def __init__(self, name=None, module=None, attributes=None):
if name is not None:
spec = 'x:' + name
else:
spec = None
ReferenceKind.__init__(self, spec)
self.module = module
self.name = name
self.imported_from = None
self.methods = []
self.enums = []
self.constants = []
self.attributes = attributes
def AddMethod(self, name, ordinal=None, attributes=None):
method = Method(self, name, ordinal, attributes)
self.methods.append(method)
return method
# TODO(451323): Remove when the language backends no longer rely on this.
@property
def client(self):
return None
class EnumField(object):
def __init__(self, name=None, value=None, attributes=None,
numeric_value=None):
self.name = name
self.value = value
self.attributes = attributes
self.numeric_value = numeric_value
class Enum(Kind):
def __init__(self, name=None, module=None, attributes=None):
self.module = module
self.name = name
self.imported_from = None
if name is not None:
spec = 'x:' + name
else:
spec = None
Kind.__init__(self, spec)
self.fields = []
self.attributes = attributes
class Module(object):
def __init__(self, name=None, namespace=None, attributes=None):
self.name = name
self.path = name
self.namespace = namespace
self.structs = []
self.unions = []
self.interfaces = []
self.enums = []
self.constants = []
self.kinds = {}
self.attributes = attributes
self.imports = []
# Transitive imports should contain all of the module's imports plus
# all of their imports recursively.
self.transitive_imports = []
def AddInterface(self, name, attributes=None):
interface = Interface(name, self, attributes)
self.interfaces.append(interface)
return interface
def AddStruct(self, name, attributes=None):
struct = Struct(name, self, attributes)
self.structs.append(struct)
return struct
def AddUnion(self, name, attributes=None):
union = Union(name, self, attributes)
self.unions.append(union)
return union
def GetPackageName(kind):
"""Get the package name from the given kind's module."""
return kind.module.name.split('.')[0]
def IsBoolKind(kind):
return kind.spec == BOOL.spec
def IsFloatKind(kind):
return kind.spec == FLOAT.spec
def IsDoubleKind(kind):
return kind.spec == DOUBLE.spec
def IsIntegralKind(kind):
return (kind.spec == BOOL.spec or
kind.spec == INT8.spec or
kind.spec == INT16.spec or
kind.spec == INT32.spec or
kind.spec == INT64.spec or
kind.spec == UINT8.spec or
kind.spec == UINT16.spec or
kind.spec == UINT32.spec or
kind.spec == UINT64.spec)
def IsNumericalKind(kind):
return (IsBoolKind(kind) or
IsFloatKind(kind) or
IsDoubleKind(kind) or
IsIntegralKind(kind))
def IsStringKind(kind):
return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec
def IsGenericHandleKind(kind):
return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec
def IsDataPipeConsumerKind(kind):
return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec
def IsDataPipeProducerKind(kind):
return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec
def IsMessagePipeKind(kind):
return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec
def IsSharedBufferKind(kind):
return (kind.spec == SHAREDBUFFER.spec or
kind.spec == NULLABLE_SHAREDBUFFER.spec)
def IsStructKind(kind):
return isinstance(kind, Struct)
def IsUnionKind(kind):
return isinstance(kind, Union)
def IsArrayKind(kind):
return isinstance(kind, Array)
def IsInterfaceKind(kind):
return isinstance(kind, Interface)
def IsInterfaceRequestKind(kind):
return isinstance(kind, InterfaceRequest)
def IsEnumKind(kind):
return isinstance(kind, Enum)
def IsReferenceKind(kind):
return isinstance(kind, ReferenceKind)
def IsNullableKind(kind):
return IsReferenceKind(kind) and kind.is_nullable
def IsMapKind(kind):
return isinstance(kind, Map)
def IsObjectKind(kind):
return IsPointerKind(kind) or IsUnionKind(kind)
def IsPointerKind(kind):
return (IsStructKind(kind) or IsArrayKind(kind) or IsStringKind(kind) or
IsMapKind(kind))
# Please note that interface is not considered as handle kind, since it is an
# aggregate type consisting of a handle and a version number.
def IsAnyHandleKind(kind):
return (IsGenericHandleKind(kind) or
IsDataPipeConsumerKind(kind) or
IsDataPipeProducerKind(kind) or
IsMessagePipeKind(kind) or
IsSharedBufferKind(kind) or
IsInterfaceRequestKind(kind))
def IsMoveOnlyKind(kind):
return (not IsStringKind(kind) and IsObjectKind(kind)) or \
IsAnyHandleKind(kind) or IsInterfaceKind(kind)
def ContainsHandles(kind, visited_kinds):
if kind in visited_kinds:
# No need to examine the kind again.
return False
visited_kinds.add(kind)
if IsAnyHandleKind(kind) or IsInterfaceKind(kind):
return True
if IsArrayKind(kind):
return ContainsHandles(kind.kind, visited_kinds)
if IsStructKind(kind) or IsUnionKind(kind):
for field in kind.fields:
if ContainsHandles(field.kind, visited_kinds):
return True
if IsMapKind(kind):
# No need to examine the key kind, only primitive kinds and non-nullable
# string are allowed to be key kinds.
return ContainsHandles(kind.value_kind, visited_kinds)
return False
def IsCloneableKind(kind):
return not ContainsHandles(kind, set())
def HasCallbacks(interface):
for method in interface.methods:
if method.response_parameters != None:
return True
return False