1 # Copyright (c) 2012 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.
8 from model
import PropertyType
12 class HGenerator(object):
13 def __init__(self
, type_generator
, cpp_namespace_pattern
):
14 self
._type
_generator
= type_generator
15 self
._cpp
_namespace
_pattern
= cpp_namespace_pattern
17 def Generate(self
, namespace
):
18 return _Generator(namespace
,
20 self
._cpp
_namespace
_pattern
).Generate()
23 class _Generator(object):
24 """A .h generator for a namespace.
26 def __init__(self
, namespace
, cpp_type_generator
, cpp_namespace_pattern
):
27 self
._namespace
= namespace
28 self
._type
_helper
= cpp_type_generator
29 self
._cpp
_namespace
_pattern
= cpp_namespace_pattern
30 self
._generate
_error
_messages
= namespace
.compiler_options
.get(
31 'generate_error_messages', False)
34 """Generates a Code object with the .h for a single namespace.
37 (c
.Append(cpp_util
.CHROMIUM_LICENSE
)
39 .Append(cpp_util
.GENERATED_FILE_MESSAGE
% self
._namespace
.source_file
)
43 # Hack: for the purpose of gyp the header file will always be the source
44 # file with its file extension replaced by '.h'. Assume so.
45 output_file
= os
.path
.splitext(self
._namespace
.source_file
)[0] + '.h'
46 ifndef_name
= cpp_util
.GenerateIfndefName(output_file
)
48 (c
.Append('#ifndef %s' % ifndef_name
)
49 .Append('#define %s' % ifndef_name
)
51 .Append('#include <map>')
52 .Append('#include <string>')
53 .Append('#include <vector>')
55 .Append('#include "base/basictypes.h"')
56 .Append('#include "base/logging.h"')
57 .Append('#include "base/memory/linked_ptr.h"')
58 .Append('#include "base/memory/scoped_ptr.h"')
59 .Append('#include "base/values.h"')
60 .Cblock(self
._type
_helper
.GenerateIncludes())
64 # TODO(calamity): These forward declarations should be #includes to allow
65 # $ref types from other files to be used as required params. This requires
66 # some detangling of windows and tabs which will currently lead to circular
68 c
.Cblock(self
._type
_helper
.GenerateForwardDeclarations(
69 self
._cpp
_namespace
_pattern
))
71 cpp_namespace
= cpp_util
.GetCppNamespace(self
._cpp
_namespace
_pattern
,
72 self
._namespace
.unix_name
)
73 c
.Concat(cpp_util
.OpenNamespace(cpp_namespace
))
75 if self
._namespace
.properties
:
77 .Append('// Properties')
81 for property in self
._namespace
.properties
.values():
82 property_code
= self
._type
_helper
.GeneratePropertyValues(
84 'extern const %(type)s %(name)s;')
86 c
.Cblock(property_code
)
87 if self
._namespace
.types
:
92 .Cblock(self
._GenerateTypes
(self
._FieldDependencyOrder
(),
94 generate_typedefs
=True))
96 if self
._namespace
.functions
:
98 .Append('// Functions')
102 for function
in self
._namespace
.functions
.values():
103 c
.Cblock(self
._GenerateFunction
(function
))
104 if self
._namespace
.events
:
110 for event
in self
._namespace
.events
.values():
111 c
.Cblock(self
._GenerateEvent
(event
))
112 (c
.Concat(cpp_util
.CloseNamespace(cpp_namespace
))
113 .Append('#endif // %s' % ifndef_name
)
118 def _FieldDependencyOrder(self
):
119 """Generates the list of types in the current namespace in an order in which
120 depended-upon types appear before types which depend on them.
122 dependency_order
= []
124 def ExpandType(path
, type_
):
126 raise ValueError("Illegal circular dependency via cycle " +
127 ", ".join(map(lambda x
: x
.name
, path
+ [type_
])))
128 for prop
in type_
.properties
.values():
129 if (prop
.type_
== PropertyType
.REF
and
130 schema_util
.GetNamespace(prop
.ref_type
) == self
._namespace
.name
):
131 ExpandType(path
+ [type_
], self
._namespace
.types
[prop
.ref_type
])
132 if not type_
in dependency_order
:
133 dependency_order
.append(type_
)
135 for type_
in self
._namespace
.types
.values():
136 ExpandType([], type_
)
137 return dependency_order
139 def _GenerateEnumDeclaration(self
, enum_name
, type_
):
140 """Generate a code object with the declaration of a C++ enum.
143 c
.Sblock('enum %s {' % enum_name
)
144 c
.Append(self
._type
_helper
.GetEnumNoneValue(type_
) + ',')
145 for value
in type_
.enum_values
:
146 current_enum_string
= self
._type
_helper
.GetEnumValue(type_
, value
)
147 c
.Append(current_enum_string
+ ',')
148 c
.Append('%s = %s,' % (
149 self
._type
_helper
.GetEnumLastValue(type_
), current_enum_string
))
153 def _GenerateFields(self
, props
):
154 """Generates the field declarations when declaring a type.
157 needs_blank_line
= False
161 needs_blank_line
= True
163 c
.Comment(prop
.description
)
164 # ANY is a base::Value which is abstract and cannot be a direct member, so
165 # we always need to wrap it in a scoped_ptr.
166 is_ptr
= prop
.optional
or prop
.type_
.property_type
== PropertyType
.ANY
167 (c
.Append('%s %s;' % (
168 self
._type
_helper
.GetCppType(prop
.type_
, is_ptr
=is_ptr
),
173 def _GenerateType(self
, type_
, is_toplevel
=False, generate_typedefs
=False):
174 """Generates a struct for |type_|.
176 |is_toplevel| implies that the type was declared in the "types" field
177 of an API schema. This determines the correct function
179 |generate_typedefs| controls whether primitive types should be generated as
180 a typedef. This may not always be desired. If false,
181 primitive types are ignored.
183 classname
= cpp_util
.Classname(schema_util
.StripNamespace(type_
.name
))
187 # Wrap functions within types in the type's namespace.
188 (c
.Append('namespace %s {' % classname
)
191 for function
in type_
.functions
.values():
192 c
.Cblock(self
._GenerateFunction
(function
))
193 c
.Append('} // namespace %s' % classname
)
194 elif type_
.property_type
== PropertyType
.ARRAY
:
195 if generate_typedefs
and type_
.description
:
196 c
.Comment(type_
.description
)
197 c
.Cblock(self
._GenerateType
(type_
.item_type
))
198 if generate_typedefs
:
199 (c
.Append('typedef std::vector<%s > %s;' % (
200 self
._type
_helper
.GetCppType(type_
.item_type
),
203 elif type_
.property_type
== PropertyType
.STRING
:
204 if generate_typedefs
:
205 if type_
.description
:
206 c
.Comment(type_
.description
)
207 c
.Append('typedef std::string %(classname)s;')
208 elif type_
.property_type
== PropertyType
.ENUM
:
209 if type_
.description
:
210 c
.Comment(type_
.description
)
211 c
.Cblock(self
._GenerateEnumDeclaration
(classname
, type_
));
212 # Top level enums are in a namespace scope so the methods shouldn't be
213 # static. On the other hand, those declared inline (e.g. in an object) do.
214 maybe_static
= '' if is_toplevel
else 'static '
216 .Append('%sstd::string ToString(%s as_enum);' %
217 (maybe_static
, classname
))
218 .Append('%s%s Parse%s(const std::string& as_string);' %
219 (maybe_static
, classname
, classname
))
221 elif type_
.property_type
in (PropertyType
.CHOICES
,
222 PropertyType
.OBJECT
):
223 if type_
.description
:
224 c
.Comment(type_
.description
)
225 (c
.Sblock('struct %(classname)s {')
226 .Append('%(classname)s();')
227 .Append('~%(classname)s();')
229 if type_
.origin
.from_json
:
231 .Comment('Populates a %s object from a base::Value. Returns'
232 ' whether |out| was successfully populated.' % classname
)
233 .Append('static bool Populate(%s);' % self
._GenerateParams
(
234 ('const base::Value& value', '%s* out' % classname
)))
238 .Comment('Creates a %s object from a base::Value, or NULL on '
239 'failure.' % classname
)
240 .Append('static scoped_ptr<%s> FromValue(%s);' % (
241 classname
, self
._GenerateParams
(('const base::Value& value',))))
243 if type_
.origin
.from_client
:
244 value_type
= ('base::Value'
245 if type_
.property_type
is PropertyType
.CHOICES
else
246 'base::DictionaryValue')
248 .Comment('Returns a new %s representing the serialized form of this '
249 '%s object.' % (value_type
, classname
))
250 .Append('scoped_ptr<%s> ToValue() const;' % value_type
)
252 if type_
.property_type
== PropertyType
.CHOICES
:
253 # Choices are modelled with optional fields for each choice. Exactly one
254 # field of the choice is guaranteed to be set by the compiler.
255 c
.Cblock(self
._GenerateTypes
(type_
.choices
))
256 c
.Append('// Choices:')
257 for choice_type
in type_
.choices
:
258 c
.Append('%s as_%s;' % (
259 self
._type
_helper
.GetCppType(choice_type
, is_ptr
=True),
260 choice_type
.unix_name
))
262 properties
= type_
.properties
.values()
264 .Cblock(self
._GenerateTypes
(p
.type_
for p
in properties
))
265 .Cblock(self
._GenerateFields
(properties
)))
266 if type_
.additional_properties
is not None:
267 # Most additionalProperties actually have type "any", which is better
268 # modelled as a DictionaryValue rather than a map of string -> Value.
269 if type_
.additional_properties
.property_type
== PropertyType
.ANY
:
270 c
.Append('base::DictionaryValue additional_properties;')
272 (c
.Cblock(self
._GenerateType
(type_
.additional_properties
))
273 .Append('std::map<std::string, %s> additional_properties;' %
274 cpp_util
.PadForGenerics(
275 self
._type
_helper
.GetCppType(type_
.additional_properties
,
276 is_in_container
=True)))
281 .Append('DISALLOW_COPY_AND_ASSIGN(%(classname)s);')
284 return c
.Substitute({'classname': classname
})
286 def _GenerateEvent(self
, event
):
287 """Generates the namespaces for an event.
290 # TODO(kalman): use event.unix_name not Classname.
291 event_namespace
= cpp_util
.Classname(event
.name
)
292 (c
.Append('namespace %s {' % event_namespace
)
294 .Concat(self
._GenerateEventNameConstant
(event
))
295 .Concat(self
._GenerateCreateCallbackArguments
(event
))
296 .Eblock('} // namespace %s' % event_namespace
)
300 def _GenerateFunction(self
, function
):
301 """Generates the namespaces and structs for a function.
304 # TODO(kalman): Use function.unix_name not Classname here.
305 function_namespace
= cpp_util
.Classname(function
.name
)
306 # Windows has a #define for SendMessage, so to avoid any issues, we need
307 # to not use the name.
308 if function_namespace
== 'SendMessage':
309 function_namespace
= 'PassMessage'
310 (c
.Append('namespace %s {' % function_namespace
)
312 .Cblock(self
._GenerateFunctionParams
(function
))
314 if function
.callback
:
315 c
.Cblock(self
._GenerateFunctionResults
(function
.callback
))
316 c
.Append('} // namespace %s' % function_namespace
)
319 def _GenerateFunctionParams(self
, function
):
320 """Generates the struct for passing parameters from JSON to a function.
322 if not function
.params
:
326 (c
.Sblock('struct Params {')
327 .Append('static scoped_ptr<Params> Create(%s);' % self
._GenerateParams
(
328 ('const base::ListValue& args',)))
329 .Append('~Params();')
331 .Cblock(self
._GenerateTypes
(p
.type_
for p
in function
.params
))
332 .Cblock(self
._GenerateFields
(function
.params
))
338 .Append('DISALLOW_COPY_AND_ASSIGN(Params);')
343 def _GenerateTypes(self
, types
, is_toplevel
=False, generate_typedefs
=False):
344 """Generate the structures required by a property such as OBJECT classes
349 c
.Cblock(self
._GenerateType
(type_
,
350 is_toplevel
=is_toplevel
,
351 generate_typedefs
=generate_typedefs
))
354 def _GenerateCreateCallbackArguments(self
, function
):
355 """Generates functions for passing parameters to a callback.
358 params
= function
.params
359 c
.Cblock(self
._GenerateTypes
((p
.type_
for p
in params
), is_toplevel
=True))
361 declaration_list
= []
363 if param
.description
:
364 c
.Comment(param
.description
)
365 declaration_list
.append(cpp_util
.GetParameterDeclaration(
366 param
, self
._type
_helper
.GetCppType(param
.type_
)))
367 c
.Append('scoped_ptr<base::ListValue> Create(%s);' %
368 ', '.join(declaration_list
))
371 def _GenerateEventNameConstant(self
, event
):
372 """Generates a constant string array for the event name.
375 c
.Append('extern const char kEventName[]; // "%s.%s"' % (
376 self
._namespace
.name
, event
.name
))
380 def _GenerateFunctionResults(self
, callback
):
381 """Generates namespace for passing a function's result back.
384 (c
.Append('namespace Results {')
386 .Concat(self
._GenerateCreateCallbackArguments
(callback
))
387 .Append('} // namespace Results')
391 def _GenerateParams(self
, params
):
392 """Builds the parameter list for a function, given an array of parameters.
394 # |error| is populated with warnings and/or errors found during parsing.
395 # |error| being set does not necessarily imply failure and may be
397 # For example, optional properties may have failed to parse, but the
398 # parser was able to continue.
399 if self
._generate
_error
_messages
:
400 params
+= ('base::string16* error',)
401 return ', '.join(str(p
) for p
in params
)