Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / json_schema_compiler / cpp_bundle_generator.py
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.
4
5 import code
6 import cpp_util
7 from model import Platforms
8 from schema_util import CapitalizeFirstLetter
9 from schema_util import JsFunctionNameToClassName
10
11 import json
12 import os
13 import re
14
15
16 def _RemoveDescriptions(node):
17   """Returns a copy of |schema| with "description" fields removed.
18   """
19   if isinstance(node, dict):
20     result = {}
21     for key, value in node.items():
22       # Some schemas actually have properties called "description", so only
23       # remove descriptions that have string values.
24       if key == 'description' and isinstance(value, basestring):
25         continue
26       result[key] = _RemoveDescriptions(value)
27     return result
28   if isinstance(node, list):
29     return [_RemoveDescriptions(v) for v in node]
30   return node
31
32
33 class CppBundleGenerator(object):
34   """This class contains methods to generate code based on multiple schemas.
35   """
36
37   def __init__(self,
38                root,
39                model,
40                api_defs,
41                cpp_type_generator,
42                cpp_namespace,
43                source_file_dir,
44                impl_dir):
45     self._root = root
46     self._model = model
47     self._api_defs = api_defs
48     self._cpp_type_generator = cpp_type_generator
49     self._cpp_namespace = cpp_namespace
50     self._source_file_dir = source_file_dir
51     self._impl_dir = impl_dir
52
53     self.api_cc_generator = _APICCGenerator(self)
54     self.api_h_generator = _APIHGenerator(self)
55     self.schemas_cc_generator = _SchemasCCGenerator(self)
56     self.schemas_h_generator = _SchemasHGenerator(self)
57
58   def _GenerateHeader(self, file_base, body_code):
59     """Generates a code.Code object for a header file
60
61     Parameters:
62     - |file_base| - the base of the filename, e.g. 'foo' (for 'foo.h')
63     - |body_code| - the code to put in between the multiple inclusion guards"""
64     c = code.Code()
65     c.Append(cpp_util.CHROMIUM_LICENSE)
66     c.Append()
67     c.Append(cpp_util.GENERATED_BUNDLE_FILE_MESSAGE % self._source_file_dir)
68     ifndef_name = cpp_util.GenerateIfndefName(self._source_file_dir, file_base)
69     c.Append()
70     c.Append('#ifndef %s' % ifndef_name)
71     c.Append('#define %s' % ifndef_name)
72     c.Append()
73     c.Concat(body_code)
74     c.Append()
75     c.Append('#endif  // %s' % ifndef_name)
76     c.Append()
77     return c
78
79   def _GetPlatformIfdefs(self, model_object):
80     """Generates the "defined" conditional for an #if check if |model_object|
81     has platform restrictions. Returns None if there are no restrictions.
82     """
83     if model_object.platforms is None:
84       return None
85     ifdefs = []
86     for platform in model_object.platforms:
87       if platform == Platforms.CHROMEOS:
88         ifdefs.append('defined(OS_CHROMEOS)')
89       elif platform == Platforms.LINUX:
90         ifdefs.append('defined(OS_LINUX)')
91       elif platform == Platforms.MAC:
92         ifdefs.append('defined(OS_MACOSX)')
93       elif platform == Platforms.WIN:
94         ifdefs.append('defined(OS_WIN)')
95       else:
96         raise ValueError("Unsupported platform ifdef: %s" % platform.name)
97     return ' || '.join(ifdefs)
98
99   def _GenerateRegisterFunctions(self, namespace_name, function):
100     c = code.Code()
101     function_ifdefs = self._GetPlatformIfdefs(function)
102     if function_ifdefs is not None:
103       c.Append("#if %s" % function_ifdefs, indent_level=0)
104
105     function_name = JsFunctionNameToClassName(namespace_name, function.name)
106     c.Append("registry->RegisterFunction<%sFunction>();" % (
107         function_name))
108
109     if function_ifdefs is not None:
110       c.Append("#endif  // %s" % function_ifdefs, indent_level=0)
111     return c
112
113   def _GenerateFunctionRegistryRegisterAll(self):
114     c = code.Code()
115     c.Append('// static')
116     c.Sblock('void GeneratedFunctionRegistry::RegisterAll('
117                  'ExtensionFunctionRegistry* registry) {')
118     for namespace in self._model.namespaces.values():
119       namespace_ifdefs = self._GetPlatformIfdefs(namespace)
120       if namespace_ifdefs is not None:
121         c.Append("#if %s" % namespace_ifdefs, indent_level=0)
122
123       namespace_name = CapitalizeFirstLetter(namespace.name.replace(
124           "experimental.", ""))
125       for function in namespace.functions.values():
126         if function.nocompile:
127           continue
128         c.Concat(self._GenerateRegisterFunctions(namespace.name, function))
129
130       for type_ in namespace.types.values():
131         for function in type_.functions.values():
132           if function.nocompile:
133             continue
134           namespace_types_name = JsFunctionNameToClassName(
135                 namespace.name, type_.name)
136           c.Concat(self._GenerateRegisterFunctions(namespace_types_name,
137                                                    function))
138
139       if namespace_ifdefs is not None:
140         c.Append("#endif  // %s" % namespace_ifdefs, indent_level=0)
141     c.Eblock("}")
142     return c
143
144
145 class _APIHGenerator(object):
146   """Generates the header for API registration / declaration"""
147   def __init__(self, cpp_bundle):
148     self._bundle = cpp_bundle
149
150   def Generate(self, namespace):
151     c = code.Code()
152
153     c.Append('#include <string>')
154     c.Append()
155     c.Append('#include "base/basictypes.h"')
156     c.Append()
157     c.Append("class ExtensionFunctionRegistry;")
158     c.Append()
159     c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
160     c.Append()
161     c.Append('class GeneratedFunctionRegistry {')
162     c.Sblock(' public:')
163     c.Append('static void RegisterAll('
164                  'ExtensionFunctionRegistry* registry);')
165     c.Eblock('};')
166     c.Append()
167     c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
168     return self._bundle._GenerateHeader('generated_api', c)
169
170
171 class _APICCGenerator(object):
172   """Generates a code.Code object for the generated API .cc file"""
173
174   def __init__(self, cpp_bundle):
175     self._bundle = cpp_bundle
176
177   def Generate(self, namespace):
178     c = code.Code()
179     c.Append(cpp_util.CHROMIUM_LICENSE)
180     c.Append()
181     c.Append('#include "%s"' % (os.path.join(self._bundle._source_file_dir,
182                                              'generated_api.h')))
183     c.Append()
184     for namespace in self._bundle._model.namespaces.values():
185       namespace_name = namespace.unix_name.replace("experimental_", "")
186       implementation_header = namespace.compiler_options.get(
187           "implemented_in",
188           "%s/%s/%s_api.h" % (self._bundle._impl_dir,
189                               namespace_name,
190                               namespace_name))
191       if not os.path.exists(
192           os.path.join(self._bundle._root,
193                        os.path.normpath(implementation_header))):
194         if "implemented_in" in namespace.compiler_options:
195           raise ValueError('Header file for namespace "%s" specified in '
196                           'compiler_options not found: %s' %
197                           (namespace.unix_name, implementation_header))
198         continue
199       ifdefs = self._bundle._GetPlatformIfdefs(namespace)
200       if ifdefs is not None:
201         c.Append("#if %s" % ifdefs, indent_level=0)
202
203       c.Append('#include "%s"' % implementation_header)
204
205       if ifdefs is not None:
206         c.Append("#endif  // %s" % ifdefs, indent_level=0)
207     c.Append()
208     c.Append('#include '
209                  '"extensions/browser/extension_function_registry.h"')
210     c.Append()
211     c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
212     c.Append()
213     c.Concat(self._bundle._GenerateFunctionRegistryRegisterAll())
214     c.Append()
215     c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
216     c.Append()
217     return c
218
219
220 class _SchemasHGenerator(object):
221   """Generates a code.Code object for the generated schemas .h file"""
222   def __init__(self, cpp_bundle):
223     self._bundle = cpp_bundle
224
225   def Generate(self, namespace):
226     c = code.Code()
227     c.Append('#include <map>')
228     c.Append('#include <string>')
229     c.Append()
230     c.Append('#include "base/strings/string_piece.h"')
231     c.Append()
232     c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
233     c.Append()
234     c.Append('class GeneratedSchemas {')
235     c.Sblock(' public:')
236     c.Append('// Determines if schema named |name| is generated.')
237     c.Append('static bool IsGenerated(std::string name);')
238     c.Append()
239     c.Append('// Gets the API schema named |name|.')
240     c.Append('static base::StringPiece Get(const std::string& name);')
241     c.Eblock('};')
242     c.Append()
243     c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
244     return self._bundle._GenerateHeader('generated_schemas', c)
245
246
247 def _FormatNameAsConstant(name):
248   """Formats a name to be a C++ constant of the form kConstantName"""
249   name = '%s%s' % (name[0].upper(), name[1:])
250   return 'k%s' % re.sub('_[a-z]',
251                         lambda m: m.group(0)[1].upper(),
252                         name.replace('.', '_'))
253
254
255 class _SchemasCCGenerator(object):
256   """Generates a code.Code object for the generated schemas .cc file"""
257
258   def __init__(self, cpp_bundle):
259     self._bundle = cpp_bundle
260
261   def Generate(self, namespace):
262     c = code.Code()
263     c.Append(cpp_util.CHROMIUM_LICENSE)
264     c.Append()
265     c.Append('#include "%s"' % (os.path.join(self._bundle._source_file_dir,
266                                              'generated_schemas.h')))
267     c.Append()
268     c.Append('#include "base/lazy_instance.h"')
269     c.Append()
270     c.Append('namespace {')
271     for api in self._bundle._api_defs:
272       namespace = self._bundle._model.namespaces[api.get('namespace')]
273       # JSON parsing code expects lists of schemas, so dump a singleton list.
274       json_content = json.dumps([_RemoveDescriptions(api)],
275                                 separators=(',', ':'))
276       # Escape all double-quotes and backslashes. For this to output a valid
277       # JSON C string, we need to escape \ and ". Note that some schemas are
278       # too large to compile on windows. Split the JSON up into several
279       # strings, since apparently that helps.
280       max_length = 8192
281       segments = [json_content[i:i + max_length].replace('\\', '\\\\')
282                                                 .replace('"', '\\"')
283                   for i in xrange(0, len(json_content), max_length)]
284       c.Append('const char %s[] = "%s";' %
285           (_FormatNameAsConstant(namespace.name), '" "'.join(segments)))
286     c.Append('}')
287     c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
288     c.Append()
289     c.Sblock('struct Static {')
290     c.Sblock('Static() {')
291     for api in self._bundle._api_defs:
292       namespace = self._bundle._model.namespaces[api.get('namespace')]
293       c.Append('schemas["%s"] = %s;' % (namespace.name,
294                                         _FormatNameAsConstant(namespace.name)))
295     c.Eblock('}')
296     c.Append()
297     c.Append('std::map<std::string, const char*> schemas;')
298     c.Eblock('};')
299     c.Append()
300     c.Append('base::LazyInstance<Static> g_lazy_instance;')
301     c.Append()
302     c.Append('// static')
303     c.Sblock('base::StringPiece GeneratedSchemas::Get('
304                   'const std::string& name) {')
305     c.Append('return IsGenerated(name) ? '
306              'g_lazy_instance.Get().schemas[name] : "";')
307     c.Eblock('}')
308     c.Append()
309     c.Append('// static')
310     c.Sblock('bool GeneratedSchemas::IsGenerated(std::string name) {')
311     c.Append('return g_lazy_instance.Get().schemas.count(name) > 0;')
312     c.Eblock('}')
313     c.Append()
314     c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
315     c.Append()
316     return c