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) 290