OLD | NEW |
(Empty) | |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import collections |
| 6 import datetime |
| 7 import os.path |
| 8 import sys |
| 9 |
| 10 import code |
| 11 import cpp_util |
| 12 import model |
| 13 |
| 14 try: |
| 15 import jinja2 |
| 16 except ImportError: |
| 17 sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', |
| 18 'third_party')) |
| 19 import jinja2 |
| 20 |
| 21 |
| 22 class _PpapiGeneratorBase(object): |
| 23 """A base class for ppapi generators. |
| 24 |
| 25 Implementations should set TEMPLATE_NAME to a string containing the name of |
| 26 the template file without its extension. The template will be rendered with |
| 27 the following symbols available: |
| 28 name: A string containing the name of the namespace. |
| 29 enums: A list of enums within the namespace. |
| 30 types: A list of types within the namespace, sorted such that no element |
| 31 depends on an earlier element. |
| 32 events: A dict of events within the namespace. |
| 33 functions: A dict of functions within the namespace. |
| 34 year: An int containing the current year. |
| 35 source_file: The name of the input file. |
| 36 """ |
| 37 |
| 38 def __init__(self, namespace): |
| 39 self._namespace = namespace |
| 40 self._required_types = {} |
| 41 self._array_types = set() |
| 42 self._optional_types = set() |
| 43 self._optional_array_types = set() |
| 44 self._dependencies = collections.OrderedDict() |
| 45 self._types = [] |
| 46 self._enums = [] |
| 47 |
| 48 self.jinja_environment = jinja2.Environment( |
| 49 loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__), |
| 50 'templates', 'ppapi'))) |
| 51 self._SetupFilters() |
| 52 self._ResolveTypeDependencies() |
| 53 |
| 54 def _SetupFilters(self): |
| 55 self.jinja_environment.filters.update({ |
| 56 'ppapi_type': self.ToPpapiType, |
| 57 'classname': cpp_util.Classname, |
| 58 'enum_value': self.EnumValueName, |
| 59 'return_type': self.GetFunctionReturnType, |
| 60 'format_param_type': self.FormatParamType, |
| 61 'needs_optional': self.NeedsOptional, |
| 62 'needs_array': self.NeedsArray, |
| 63 'needs_optional_array': self.NeedsOptionalArray, |
| 64 'has_array_outs': self.HasArrayOuts, |
| 65 }) |
| 66 |
| 67 def Render(self, template_name, values): |
| 68 generated_code = code.Code() |
| 69 template = self.jinja_environment.get_template( |
| 70 '%s.template' % template_name) |
| 71 generated_code.Append(template.render(values)) |
| 72 return generated_code |
| 73 |
| 74 def Generate(self): |
| 75 """Generates a Code object for a single namespace.""" |
| 76 return self.Render(self.TEMPLATE_NAME, { |
| 77 'name': self._namespace.name, |
| 78 'enums': self._enums, |
| 79 'types': self._types, |
| 80 'events': self._namespace.events, |
| 81 'functions': self._namespace.functions, |
| 82 # TODO(sammc): Don't change years when regenerating existing output files. |
| 83 'year': datetime.date.today().year, |
| 84 'source_file': self._namespace.source_file, |
| 85 }) |
| 86 |
| 87 def _ResolveTypeDependencies(self): |
| 88 """Calculates the transitive closure of the types in _required_types. |
| 89 |
| 90 Returns a tuple containing the list of struct types and the list of enum |
| 91 types. The list of struct types is ordered such that no type depends on a |
| 92 type later in the list. |
| 93 |
| 94 """ |
| 95 if self._namespace.functions: |
| 96 for function in self._namespace.functions.itervalues(): |
| 97 self._FindFunctionDependencies(function) |
| 98 |
| 99 if self._namespace.events: |
| 100 for event in self._namespace.events.itervalues(): |
| 101 self._FindFunctionDependencies(event) |
| 102 resolved_types = set() |
| 103 while resolved_types < set(self._required_types): |
| 104 for typename in sorted(set(self._required_types) - resolved_types): |
| 105 type_ = self._required_types[typename] |
| 106 self._dependencies.setdefault(typename, set()) |
| 107 for member in type_.properties.itervalues(): |
| 108 self._RegisterDependency(member, self._NameComponents(type_)) |
| 109 resolved_types.add(typename) |
| 110 while self._dependencies: |
| 111 for name, deps in self._dependencies.items(): |
| 112 if not deps: |
| 113 if (self._required_types[name].property_type == |
| 114 model.PropertyType.ENUM): |
| 115 self._enums.append(self._required_types[name]) |
| 116 else: |
| 117 self._types.append(self._required_types[name]) |
| 118 for deps in self._dependencies.itervalues(): |
| 119 deps.discard(name) |
| 120 del self._dependencies[name] |
| 121 break |
| 122 else: |
| 123 raise ValueError('Circular dependency %s' % self._dependencies) |
| 124 |
| 125 def _FindFunctionDependencies(self, function): |
| 126 for param in function.params: |
| 127 self._RegisterDependency(param, None) |
| 128 if function.callback: |
| 129 for param in function.callback.params: |
| 130 self._RegisterDependency(param, None) |
| 131 if function.returns: |
| 132 self._RegisterTypeDependency(function.returns, None, False, False) |
| 133 |
| 134 def _RegisterDependency(self, member, depender): |
| 135 self._RegisterTypeDependency(member.type_, depender, member.optional, False) |
| 136 |
| 137 def _RegisterTypeDependency(self, type_, depender, optional, array): |
| 138 if type_.property_type == model.PropertyType.ARRAY: |
| 139 self._RegisterTypeDependency(type_.item_type, depender, optional, True) |
| 140 elif type_.property_type == model.PropertyType.REF: |
| 141 self._RegisterTypeDependency(self._namespace.types[type_.ref_type], |
| 142 depender, optional, array) |
| 143 elif type_.property_type in (model.PropertyType.OBJECT, |
| 144 model.PropertyType.ENUM): |
| 145 name_components = self._NameComponents(type_) |
| 146 self._required_types[name_components] = type_ |
| 147 if depender: |
| 148 self._dependencies.setdefault(depender, set()).add( |
| 149 name_components) |
| 150 if array: |
| 151 self._array_types.add(name_components) |
| 152 if optional: |
| 153 self._optional_array_types.add(name_components) |
| 154 elif optional: |
| 155 self._optional_types.add(name_components) |
| 156 |
| 157 @staticmethod |
| 158 def _NameComponents(entity): |
| 159 """Returns a tuple of the fully-qualified name of an entity.""" |
| 160 names = [] |
| 161 while entity: |
| 162 if (not isinstance(entity, model.Type) or |
| 163 entity.property_type != model.PropertyType.ARRAY): |
| 164 names.append(entity.name) |
| 165 entity = entity.parent |
| 166 return tuple(reversed(names[:-1])) |
| 167 |
| 168 def ToPpapiType(self, type_, array=False, optional=False): |
| 169 """Returns a string containing the name of the Pepper C type for |type_|. |
| 170 |
| 171 If array is True, returns the name of an array of |type_|. If optional is |
| 172 True, returns the name of an optional |type_|. If both array and optional |
| 173 are True, returns the name of an optional array of |type_|. |
| 174 """ |
| 175 if isinstance(type_, model.Function) or type_.property_type in ( |
| 176 model.PropertyType.OBJECT, model.PropertyType.ENUM): |
| 177 return self._FormatPpapiTypeName( |
| 178 array, optional, '_'.join( |
| 179 cpp_util.Classname(s) for s in self._NameComponents(type_)), |
| 180 namespace=cpp_util.Classname(self._namespace.name)) |
| 181 elif type_.property_type == model.PropertyType.REF: |
| 182 return self.ToPpapiType(self._namespace.types[type_.ref_type], |
| 183 optional=optional, array=array) |
| 184 elif type_.property_type == model.PropertyType.ARRAY: |
| 185 return self.ToPpapiType(type_.item_type, array=True, |
| 186 optional=optional) |
| 187 elif type_.property_type == model.PropertyType.STRING and not array: |
| 188 return 'PP_Var' |
| 189 elif array or optional: |
| 190 if type_.property_type in self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP: |
| 191 return self._FormatPpapiTypeName( |
| 192 array, optional, |
| 193 self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP[type_.property_type], '') |
| 194 return self._PPAPI_PRIMITIVE_TYPE_MAP.get(type_.property_type, 'PP_Var') |
| 195 |
| 196 _PPAPI_PRIMITIVE_TYPE_MAP = { |
| 197 model.PropertyType.BOOLEAN: 'PP_Bool', |
| 198 model.PropertyType.DOUBLE: 'double_t', |
| 199 model.PropertyType.INT64: 'int64_t', |
| 200 model.PropertyType.INTEGER: 'int32_t', |
| 201 } |
| 202 _PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP = { |
| 203 model.PropertyType.BOOLEAN: 'Bool', |
| 204 model.PropertyType.DOUBLE: 'Double', |
| 205 model.PropertyType.INT64: 'Int64', |
| 206 model.PropertyType.INTEGER: 'Int32', |
| 207 model.PropertyType.STRING: 'String', |
| 208 } |
| 209 |
| 210 @staticmethod |
| 211 def _FormatPpapiTypeName(array, optional, name, namespace=''): |
| 212 if namespace: |
| 213 namespace = '%s_' % namespace |
| 214 if array: |
| 215 if optional: |
| 216 return 'PP_%sOptional_%s_Array' % (namespace, name) |
| 217 return 'PP_%s%s_Array' % (namespace, name) |
| 218 if optional: |
| 219 return 'PP_%sOptional_%s' % (namespace, name) |
| 220 return 'PP_%s%s' % (namespace, name) |
| 221 |
| 222 def NeedsOptional(self, type_): |
| 223 """Returns True if an optional |type_| is required.""" |
| 224 return self._NameComponents(type_) in self._optional_types |
| 225 |
| 226 def NeedsArray(self, type_): |
| 227 """Returns True if an array of |type_| is required.""" |
| 228 return self._NameComponents(type_) in self._array_types |
| 229 |
| 230 def NeedsOptionalArray(self, type_): |
| 231 """Returns True if an optional array of |type_| is required.""" |
| 232 return self._NameComponents(type_) in self._optional_array_types |
| 233 |
| 234 def FormatParamType(self, param): |
| 235 """Formats the type of a parameter or property.""" |
| 236 return self.ToPpapiType(param.type_, optional=param.optional) |
| 237 |
| 238 @staticmethod |
| 239 def GetFunctionReturnType(function): |
| 240 return 'int32_t' if function.callback or function.returns else 'void' |
| 241 |
| 242 def EnumValueName(self, enum_value, enum_type): |
| 243 """Returns a string containing the name for an enum value.""" |
| 244 return '%s_%s' % (self.ToPpapiType(enum_type).upper(), |
| 245 enum_value.name.upper()) |
| 246 |
| 247 def _ResolveType(self, type_): |
| 248 if type_.property_type == model.PropertyType.REF: |
| 249 return self._ResolveType(self._namespace.types[type_.ref_type]) |
| 250 if type_.property_type == model.PropertyType.ARRAY: |
| 251 return self._ResolveType(type_.item_type) |
| 252 return type_ |
| 253 |
| 254 def _IsOrContainsArray(self, type_): |
| 255 if type_.property_type == model.PropertyType.ARRAY: |
| 256 return True |
| 257 type_ = self._ResolveType(type_) |
| 258 if type_.property_type == model.PropertyType.OBJECT: |
| 259 return any(self._IsOrContainsArray(param.type_) |
| 260 for param in type_.properties.itervalues()) |
| 261 return False |
| 262 |
| 263 def HasArrayOuts(self, function): |
| 264 """Returns True if the function produces any arrays as outputs. |
| 265 |
| 266 This includes arrays that are properties of other objects. |
| 267 """ |
| 268 if function.callback: |
| 269 for param in function.callback.params: |
| 270 if self._IsOrContainsArray(param.type_): |
| 271 return True |
| 272 return function.returns and self._IsOrContainsArray(function.returns) |
| 273 |
| 274 |
| 275 class _IdlGenerator(_PpapiGeneratorBase): |
| 276 TEMPLATE_NAME = 'idl' |
| 277 |
| 278 |
| 279 class _GeneratorWrapper(object): |
| 280 def __init__(self, generator_factory): |
| 281 self._generator_factory = generator_factory |
| 282 |
| 283 def Generate(self, namespace): |
| 284 return self._generator_factory(namespace).Generate() |
| 285 |
| 286 |
| 287 class PpapiGenerator(object): |
| 288 def __init__(self): |
| 289 self.idl_generator = _GeneratorWrapper(_IdlGenerator) |
OLD | NEW |