Revert safe-browsing PrefixSet size_t handling.
[chromium-blink-merge.git] / tools / json_schema_compiler / ppapi_generator.py
blobe47b9540e34654e1dceb617e8a4d2d3e7f571d42
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.
5 import collections
6 import datetime
7 import os.path
8 import sys
10 import code
11 import cpp_util
12 import model
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
22 class _PpapiGeneratorBase(object):
23 """A base class for ppapi generators.
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 """
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 = []
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()
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,
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
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,
87 def _ResolveTypeDependencies(self):
88 """Calculates the transitive closure of the types in _required_types.
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.
94 """
95 if self._namespace.functions:
96 for function in self._namespace.functions.itervalues():
97 self._FindFunctionDependencies(function)
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)
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)
134 def _RegisterDependency(self, member, depender):
135 self._RegisterTypeDependency(member.type_, depender, member.optional, False)
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)
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]))
168 def ToPpapiType(self, type_, array=False, optional=False):
169 """Returns a string containing the name of the Pepper C type for |type_|.
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_|.
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')
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',
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',
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)
222 def NeedsOptional(self, type_):
223 """Returns True if an optional |type_| is required."""
224 return self._NameComponents(type_) in self._optional_types
226 def NeedsArray(self, type_):
227 """Returns True if an array of |type_| is required."""
228 return self._NameComponents(type_) in self._array_types
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
234 def FormatParamType(self, param):
235 """Formats the type of a parameter or property."""
236 return self.ToPpapiType(param.type_, optional=param.optional)
238 @staticmethod
239 def GetFunctionReturnType(function):
240 return 'int32_t' if function.callback or function.returns else 'void'
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())
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_
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
263 def HasArrayOuts(self, function):
264 """Returns True if the function produces any arrays as outputs.
266 This includes arrays that are properties of other objects.
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)
275 class _IdlGenerator(_PpapiGeneratorBase):
276 TEMPLATE_NAME = 'idl'
279 class _GeneratorWrapper(object):
280 def __init__(self, generator_factory):
281 self._generator_factory = generator_factory
283 def Generate(self, namespace):
284 return self._generator_factory(namespace).Generate()
287 class PpapiGenerator(object):
288 def __init__(self):
289 self.idl_generator = _GeneratorWrapper(_IdlGenerator)