Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: tools/json_schema_compiler/ppapi_generator.py

Issue 101483003: Add a Pepper IDL generator to the JSON schema compiler. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
9 import code
10 import cpp_util
11 import model
12
13 try:
14 import jinja2
15 except ImportError as e:
16 jinja2 = None
17 jinja2_error = e
18
19
20 class _PpapiGeneratorBase(object):
not at google - send to devlin 2013/12/09 23:30:58 docstring
Sam McNally 2013/12/10 09:41:15 Done.
21 def __init__(self, namespace, is_dev):
22 self.is_dev = is_dev
23 self._namespace = namespace
24 self._required_types = {}
25 self._array_types = set()
26 self._optional_types = set()
27 self._optional_array_types = set()
28 self._dependencies = collections.OrderedDict()
29 self._types = []
30 self._enums = []
31
32 if not jinja2:
33 raise jinja2_error
not at google - send to devlin 2013/12/09 23:30:58 this will reset the stack trace. if you want the o
Sam McNally 2013/12/10 09:41:15 Done.
34 self.jinja_environment = jinja2.Environment(
35 loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__),
36 'templates', 'ppapi')))
37 self._SetupFilters()
38 self._ResolveTypeDependencies()
39
40 def _SetupFilters(self):
41 self.jinja_environment.filters['ppapi_type'] = self.ToPpapiType
42 self.jinja_environment.filters['classname'] = cpp_util.Classname
43 self.jinja_environment.filters['dev_suffix'] = self.AddDevSuffix
44 self.jinja_environment.filters['enum_value'] = self.EnumValueName
45 self.jinja_environment.filters['return_type'] = self.GetFunctionReturnType
46 self.jinja_environment.filters['format_param_type'] = self.FormatParamType
47 self.jinja_environment.filters['needs_optional'] = self.NeedsOptional
48 self.jinja_environment.filters['needs_array'] = self.NeedsArray
49 self.jinja_environment.filters['needs_optional_array'] = (
not at google - send to devlin 2013/12/09 23:30:58 nit: self.jinja_environment.filters.update({ 'pp
Sam McNally 2013/12/10 09:41:15 Done.
50 self.NeedsOptionalArray)
51
52 def Render(self, template_name, values):
53 c = code.Code()
54 template = self.jinja_environment.get_template(
55 '%s.template' % template_name)
56 c.Append(template.render(values))
57 return c
58
59 def Generate(self):
60 """Generates a Code object for a single namespace."""
61 return self.Render(self.TEMPLATE_NAME, {
62 'name': self._namespace.name,
not at google - send to devlin 2013/12/09 23:30:58 nit: only 2 indent for object declarations not 4.
Sam McNally 2013/12/10 09:41:15 Done.
63 'enums': self._enums,
64 'types': self._types,
65 'events': self._namespace.events,
66 'functions': self._namespace.functions,
67 'year': datetime.date.today().year,
68 'source_file': self._namespace.source_file,
69 'dev': '_dev' if self.is_dev else '',
70 'dev_path': '/dev' if self.is_dev else '',
71 })
72
73 def _ResolveTypeDependencies(self):
74 """Calculates the transitive closure of the types in _required_types.
75
76 Returns a tuple containing the list of struct types and the list of enum
77 types. The list of struct types is ordered such that no type depends on a
78 type later in the list.
79
80 """
81 if self._namespace.functions:
82 for function in self._namespace.functions.values():
not at google - send to devlin 2013/12/09 23:30:58 itervalue) and iterfoo() for everywhere else it m
Sam McNally 2013/12/10 09:41:15 Done.
83 self._FindFunctionDependencies(function)
84
85 if self._namespace.events:
86 for event in self._namespace.events.values():
87 self._FindFunctionDependencies(event)
88 resolved_types = set()
89 while resolved_types < set(self._required_types):
90 for typename in sorted(set(self._required_types) - resolved_types):
91 type_ = self._required_types[typename]
92 self._dependencies.setdefault(typename, set())
93 for member in type_.properties.values():
94 self._RegisterDependency(member, self._NameComponents(type_))
95 resolved_types.add(typename)
96 while self._dependencies:
97 for name, deps in self._dependencies.items():
98 if not deps:
99 if (self._required_types[name].property_type ==
100 model.PropertyType.ENUM):
101 self._enums.append(self._required_types[name])
102 else:
103 self._types.append(self._required_types[name])
104 for deps in self._dependencies.values():
105 deps.discard(name)
106 del self._dependencies[name]
107 break
108 else:
109 raise ValueError('Circular dependency %s' % self._dependencies)
110
111 def _FindFunctionDependencies(self, function):
112 for param in function.params:
113 self._RegisterDependency(param, None)
114 if function.callback:
115 for param in function.callback.params:
116 self._RegisterDependency(param, None)
117 if function.returns:
118 self._RegisterTypeDependency(function.returns, None, False, False)
119
120 def _RegisterDependency(self, member, depender):
121 self._RegisterTypeDependency(member.type_, depender, member.optional, False)
122
123 def _RegisterTypeDependency(self, type_, depender, optional, array):
124 if type_.property_type == model.PropertyType.ARRAY:
125 self._RegisterTypeDependency(type_.item_type, depender, optional, True)
126 elif type_.property_type == model.PropertyType.REF:
127 self._RegisterTypeDependency(self._namespace.types[type_.ref_type],
128 depender, optional, array)
129 elif type_.property_type in (model.PropertyType.OBJECT,
130 model.PropertyType.ENUM):
131 name_components = self._NameComponents(type_)
132 self._required_types[name_components] = type_
133 if depender:
134 self._dependencies.setdefault(depender, set()).add(
135 name_components)
136 if array:
137 self._array_types.add(name_components)
138 if optional:
139 self._optional_array_types.add(name_components)
140 elif optional:
141 self._optional_types.add(name_components)
142
143 @staticmethod
144 def _NameComponents(entity, include_namespace=False):
145 """Returns a tuple of the fully-qualified name of an entity."""
146 names = []
147 while entity:
148 if (not isinstance(entity, model.Type) or
149 entity.property_type != model.PropertyType.ARRAY):
150 names.append(entity.name)
151 entity = entity.parent
152 if include_namespace:
153 return tuple(reversed(names))
154 return tuple(reversed(names[:-1]))
155
156 def ToPpapiType(self, type_, array=False, optional=False):
157 """Returns a string containing the name of the Pepper C type for |type_|.
158
159 If array is True, returns the name of an array of |type_|. If optional is
160 True, returns the name of an optional |type_|. If both array and optional
161 are True, returns the name of an optional array of |type_|.
162 """
163 if isinstance(type_, model.Function) or type_.property_type in (
164 model.PropertyType.OBJECT, model.PropertyType.ENUM):
165 return self._FormatPpapiTypeName(
166 array, optional, '_'.join(
167 cpp_util.Classname(s) for s in self._NameComponents(
168 type_, include_namespace=True)),
169 '_Dev' if self.is_dev else '')
170 elif type_.property_type == model.PropertyType.REF:
171 return self.ToPpapiType(self._namespace.types[type_.ref_type],
172 optional=optional, array=array)
173 elif type_.property_type == model.PropertyType.ARRAY:
174 return self.ToPpapiType(type_.item_type, array=True,
175 optional=optional)
176 elif type_.property_type == model.PropertyType.STRING and not array:
177 return 'PP_Var'
178 elif array or optional:
179 if type_.property_type in self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP:
180 return self._FormatPpapiTypeName(
181 array, optional,
182 self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP[type_.property_type], '')
183 return self._PPAPI_PRIMITIVE_TYPE_MAP.get(type_.property_type, 'PP_Var')
184
185 _PPAPI_PRIMITIVE_TYPE_MAP = {
186 model.PropertyType.BOOLEAN: 'PP_Bool',
187 model.PropertyType.DOUBLE: 'double_t',
188 model.PropertyType.INT64: 'int64_t',
189 model.PropertyType.INTEGER: 'int32_t',
190 }
191 _PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP = {
192 model.PropertyType.BOOLEAN: 'Bool',
193 model.PropertyType.DOUBLE: 'Double',
194 model.PropertyType.INT64: 'Int64',
195 model.PropertyType.INTEGER: 'Int32',
196 model.PropertyType.STRING: 'String',
197 }
198
199 @staticmethod
200 def _FormatPpapiTypeName(array, optional, *args):
201 if array:
202 if optional:
203 return 'PP_Optional_%s_Array%s' % args
204 return 'PP_%s_Array%s' % args
205 if optional:
206 return 'PP_Optional_%s%s' % args
207 return 'PP_%s%s' % args
208
209 def NeedsOptional(self, type_):
210 """Returns True if an optional |type_| is required."""
211 return self._NameComponents(type_) in self._optional_types
212
213 def NeedsArray(self, type_):
214 """Returns True if an array of |type_| is required."""
215 return self._NameComponents(type_) in self._array_types
216
217 def NeedsOptionalArray(self, type_):
218 """Returns True if an optional array of |type_| is required."""
219 return self._NameComponents(type_) in self._optional_array_types
220
221 def FormatParamType(self, param):
222 """Formats the type of a parameter or property."""
223 return self.ToPpapiType(param.type_, optional=param.optional)
224
225 def AddDevSuffix(self, name):
226 """Returns |name| with a _Dev suffix if a dev API is being generated."""
227 if self.is_dev:
228 name = '%s_Dev' % name
229 return name
230
231 @staticmethod
232 def GetFunctionReturnType(function):
233 return 'int32_t' if function.callback or function.returns else 'void'
234
235 def EnumValueName(self, enum_value, enum_type):
236 """Returns a string containing the name for an enum value."""
237 return '%s_%s' % (self.ToPpapiType(enum_type).upper(),
238 enum_value.name.upper())
239
240
241 class _IdlGenerator(_PpapiGeneratorBase):
242 TEMPLATE_NAME = 'idl'
243
244
245 class _GeneratorWrapper(object):
246 def __init__(self, generator_factory, is_dev):
247 self._generator_factory = generator_factory
248 self._is_dev = is_dev
249
250 def Generate(self, namespace):
251 return self._generator_factory(namespace, self._is_dev).Generate()
252
253
254 class PpapiGenerator(object):
255 def __init__(self, is_dev):
256 self.idl_generator = _GeneratorWrapper(_IdlGenerator, is_dev)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698