summaryrefslogtreecommitdiff
path: root/chromium/tools/json_schema_compiler
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/tools/json_schema_compiler
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/tools/json_schema_compiler')
-rw-r--r--chromium/tools/json_schema_compiler/cc_generator.py8
-rw-r--r--chromium/tools/json_schema_compiler/code.py1
-rwxr-xr-xchromium/tools/json_schema_compiler/compiler.py21
-rw-r--r--chromium/tools/json_schema_compiler/cpp_bundle_generator.py27
-rw-r--r--chromium/tools/json_schema_compiler/cpp_type_generator.py2
-rw-r--r--chromium/tools/json_schema_compiler/cpp_util.py19
-rw-r--r--chromium/tools/json_schema_compiler/features_cc_generator.py95
-rwxr-xr-xchromium/tools/json_schema_compiler/features_compiler.py76
-rw-r--r--chromium/tools/json_schema_compiler/features_h_generator.py95
-rw-r--r--chromium/tools/json_schema_compiler/idl_schema.py54
-rwxr-xr-xchromium/tools/json_schema_compiler/idl_schema_test.py69
-rw-r--r--chromium/tools/json_schema_compiler/memoize.py9
-rw-r--r--chromium/tools/json_schema_compiler/model.py90
-rwxr-xr-xchromium/tools/json_schema_compiler/model_test.py29
-rw-r--r--chromium/tools/json_schema_compiler/ppapi_generator.py289
-rwxr-xr-xchromium/tools/json_schema_compiler/ppapi_generator_test.py193
-rw-r--r--chromium/tools/json_schema_compiler/schema_loader.py30
-rw-r--r--chromium/tools/json_schema_compiler/templates/ppapi/base.template14
-rw-r--r--chromium/tools/json_schema_compiler/templates/ppapi/idl.template101
19 files changed, 1169 insertions, 53 deletions
diff --git a/chromium/tools/json_schema_compiler/cc_generator.py b/chromium/tools/json_schema_compiler/cc_generator.py
index 9935e1cc399..371c60d3032 100644
--- a/chromium/tools/json_schema_compiler/cc_generator.py
+++ b/chromium/tools/json_schema_compiler/cc_generator.py
@@ -784,7 +784,9 @@ class _Generator(object):
self._type_helper.GetEnumNoneValue(type_)))
.Concat(self._GenerateError(
'\"\'%%(key)s\': expected \\"' +
- '\\" or \\"'.join(self._type_helper.FollowRef(type_).enum_values) +
+ '\\" or \\"'.join(
+ enum_value.name
+ for enum_value in self._type_helper.FollowRef(type_).enum_values) +
'\\", got \\"" + %s + "\\""' % enum_as_string))
.Append('return %s;' % failure_value)
.Eblock('}')
@@ -820,7 +822,7 @@ class _Generator(object):
c.Sblock('switch (enum_param) {')
for enum_value in self._type_helper.FollowRef(type_).enum_values:
(c.Append('case %s: ' % self._type_helper.GetEnumValue(type_, enum_value))
- .Append(' return "%s";' % enum_value))
+ .Append(' return "%s";' % enum_value.name))
(c.Append('case %s:' % self._type_helper.GetEnumNoneValue(type_))
.Append(' return "";')
.Eblock('}')
@@ -848,7 +850,7 @@ class _Generator(object):
# This is broken up into all ifs with no else ifs because we get
# "fatal error C1061: compiler limit : blocks nested too deeply"
# on Windows.
- (c.Append('if (enum_string == "%s")' % enum_value)
+ (c.Append('if (enum_string == "%s")' % enum_value.name)
.Append(' return %s;' %
self._type_helper.GetEnumValue(type_, enum_value)))
(c.Append('return %s;' % self._type_helper.GetEnumNoneValue(type_))
diff --git a/chromium/tools/json_schema_compiler/code.py b/chromium/tools/json_schema_compiler/code.py
index 3622237a84a..8ce6afab675 100644
--- a/chromium/tools/json_schema_compiler/code.py
+++ b/chromium/tools/json_schema_compiler/code.py
@@ -133,6 +133,7 @@ class Code(object):
"""
return '\n'.join([l.value for l in self._code])
+
class Line(object):
"""A line of code.
"""
diff --git a/chromium/tools/json_schema_compiler/compiler.py b/chromium/tools/json_schema_compiler/compiler.py
index ddab77eadd7..21281fcdabf 100755
--- a/chromium/tools/json_schema_compiler/compiler.py
+++ b/chromium/tools/json_schema_compiler/compiler.py
@@ -26,11 +26,12 @@ from cpp_type_generator import CppTypeGenerator
from dart_generator import DartGenerator
import json_schema
from model import Model
+from ppapi_generator import PpapiGenerator
from schema_loader import SchemaLoader
# Names of supported code generators, as specified on the command-line.
# First is default.
-GENERATORS = ['cpp', 'cpp-bundle', 'dart']
+GENERATORS = ['cpp', 'cpp-bundle', 'dart', 'ppapi']
def GenerateSchema(generator,
filenames,
@@ -38,15 +39,14 @@ def GenerateSchema(generator,
destdir,
root_namespace,
dart_overrides_dir):
- schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(
- os.path.normpath(filenames[0]), root)))
+ schema_loader = SchemaLoader(
+ os.path.dirname(os.path.relpath(os.path.normpath(filenames[0]), root)),
+ os.path.dirname(filenames[0]))
# Merge the source files into a single list of schemas.
api_defs = []
for filename in filenames:
schema = os.path.normpath(filename)
- schema_filename, schema_extension = os.path.splitext(schema)
- path, short_filename = os.path.split(schema_filename)
- api_def = schema_loader.LoadSchema(schema)
+ api_def = schema_loader.LoadSchema(os.path.split(schema)[1])
# If compiling the C++ model code, delete 'nocompile' nodes.
if generator == 'cpp':
@@ -88,7 +88,8 @@ def GenerateSchema(generator,
api_model,
api_defs,
type_generator,
- root_namespace)
+ root_namespace,
+ namespace.source_file_dir)
generators = [
('generated_api.cc', cpp_bundle_generator.api_cc_generator),
('generated_api.h', cpp_bundle_generator.api_h_generator),
@@ -106,6 +107,12 @@ def GenerateSchema(generator,
('%s.dart' % namespace.unix_name, DartGenerator(
dart_overrides_dir))
]
+ elif generator == 'ppapi':
+ generator = PpapiGenerator()
+ generators = [
+ (os.path.join('api', 'ppb_%s.idl' % namespace.unix_name),
+ generator.idl_generator),
+ ]
else:
raise Exception('Unrecognised generator %s' % generator)
diff --git a/chromium/tools/json_schema_compiler/cpp_bundle_generator.py b/chromium/tools/json_schema_compiler/cpp_bundle_generator.py
index c713d555f72..1cadff8348d 100644
--- a/chromium/tools/json_schema_compiler/cpp_bundle_generator.py
+++ b/chromium/tools/json_schema_compiler/cpp_bundle_generator.py
@@ -12,8 +12,6 @@ import json
import os
import re
-# TODO(miket/asargent) - parameterize this.
-SOURCE_BASE_PATH = 'chrome/common/extensions/api'
def _RemoveDescriptions(node):
"""Returns a copy of |schema| with "description" fields removed.
@@ -36,12 +34,19 @@ class CppBundleGenerator(object):
"""This class contains methods to generate code based on multiple schemas.
"""
- def __init__(self, root, model, api_defs, cpp_type_generator, cpp_namespace):
+ def __init__(self,
+ root,
+ model,
+ api_defs,
+ cpp_type_generator,
+ cpp_namespace,
+ source_file_dir):
self._root = root
self._model = model
self._api_defs = api_defs
self._cpp_type_generator = cpp_type_generator
self._cpp_namespace = cpp_namespace
+ self._source_file_dir = source_file_dir
self.api_cc_generator = _APICCGenerator(self)
self.api_h_generator = _APIHGenerator(self)
@@ -57,8 +62,8 @@ class CppBundleGenerator(object):
c = code.Code()
c.Append(cpp_util.CHROMIUM_LICENSE)
c.Append()
- c.Append(cpp_util.GENERATED_BUNDLE_FILE_MESSAGE % SOURCE_BASE_PATH)
- ifndef_name = cpp_util.GenerateIfndefName(SOURCE_BASE_PATH, file_base)
+ c.Append(cpp_util.GENERATED_BUNDLE_FILE_MESSAGE % self._source_file_dir)
+ ifndef_name = cpp_util.GenerateIfndefName(self._source_file_dir, file_base)
c.Append()
c.Append('#ifndef %s' % ifndef_name)
c.Append('#define %s' % ifndef_name)
@@ -79,9 +84,15 @@ class CppBundleGenerator(object):
for platform in model_object.platforms:
if platform == Platforms.CHROMEOS:
ifdefs.append('defined(OS_CHROMEOS)')
+ elif platform == Platforms.LINUX:
+ ifdefs.append('defined(OS_LINUX)')
+ elif platform == Platforms.MAC:
+ ifdefs.append('defined(OS_MACOSX)')
+ elif platform == Platforms.WIN:
+ ifdefs.append('defined(OS_WIN)')
else:
raise ValueError("Unsupported platform ifdef: %s" % platform.name)
- return ' and '.join(ifdefs)
+ return ' || '.join(ifdefs)
def _GenerateRegisterFunctions(self, namespace_name, function):
c = code.Code()
@@ -165,7 +176,7 @@ class _APICCGenerator(object):
c = code.Code()
c.Append(cpp_util.CHROMIUM_LICENSE)
c.Append()
- c.Append('#include "%s"' % (os.path.join(SOURCE_BASE_PATH,
+ c.Append('#include "%s"' % (os.path.join(self._bundle._source_file_dir,
'generated_api.h')))
c.Append()
for namespace in self._bundle._model.namespaces.values():
@@ -248,7 +259,7 @@ class _SchemasCCGenerator(object):
c = code.Code()
c.Append(cpp_util.CHROMIUM_LICENSE)
c.Append()
- c.Append('#include "%s"' % (os.path.join(SOURCE_BASE_PATH,
+ c.Append('#include "%s"' % (os.path.join(self._bundle._source_file_dir,
'generated_schemas.h')))
c.Append()
c.Append('#include "base/lazy_instance.h"')
diff --git a/chromium/tools/json_schema_compiler/cpp_type_generator.py b/chromium/tools/json_schema_compiler/cpp_type_generator.py
index 4c0306d18bb..d485b46c5cd 100644
--- a/chromium/tools/json_schema_compiler/cpp_type_generator.py
+++ b/chromium/tools/json_schema_compiler/cpp_type_generator.py
@@ -65,7 +65,7 @@ class CppTypeGenerator(object):
e.g VAR_STRING
"""
value = '%s_%s' % (self.FollowRef(type_).unix_name.upper(),
- cpp_util.Classname(enum_value.upper()))
+ cpp_util.Classname(enum_value.name.upper()))
# To avoid collisions with built-in OS_* preprocessor definitions, we add a
# trailing slash to enum names that start with OS_.
if value.startswith("OS_"):
diff --git a/chromium/tools/json_schema_compiler/cpp_util.py b/chromium/tools/json_schema_compiler/cpp_util.py
index 2d493439344..1e7c3707883 100644
--- a/chromium/tools/json_schema_compiler/cpp_util.py
+++ b/chromium/tools/json_schema_compiler/cpp_util.py
@@ -24,6 +24,10 @@ GENERATED_BUNDLE_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITIONS IN
// %s
// DO NOT EDIT.
"""
+GENERATED_FEATURE_MESSAGE = """// GENERATED FROM THE FEATURE DEFINITIONS IN
+// %s
+// DO NOT EDIT.
+"""
def Classname(s):
"""Translates a namespace name or function name into something more
@@ -118,3 +122,18 @@ def CloseNamespace(namespace):
for component in reversed(namespace.split('::')):
c.Append('} // namespace %s' % component)
return c
+
+
+def ConstantName(feature_name):
+ """Returns a kName for a feature's name.
+ """
+ return ('k' + ''.join(word[0].upper() + word[1:]
+ for word in feature_name.replace('.', ' ').split()))
+
+
+def CamelCase(unix_name):
+ return ''.join(word.capitalize() for word in unix_name.split('_'))
+
+
+def ClassName(filepath):
+ return CamelCase(os.path.split(filepath)[1])
diff --git a/chromium/tools/json_schema_compiler/features_cc_generator.py b/chromium/tools/json_schema_compiler/features_cc_generator.py
new file mode 100644
index 00000000000..d3b3717eb92
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/features_cc_generator.py
@@ -0,0 +1,95 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os.path
+
+from code import Code
+import cpp_util
+
+
+class CCGenerator(object):
+ def Generate(self, feature_defs, source_file, namespace):
+ return _Generator(feature_defs, source_file, namespace).Generate()
+
+
+class _Generator(object):
+ """A .cc generator for features.
+ """
+ def __init__(self, feature_defs, source_file, namespace):
+ self._feature_defs = feature_defs
+ self._source_file = source_file
+ self._source_file_filename, _ = os.path.splitext(source_file)
+ self._class_name = cpp_util.ClassName(self._source_file_filename)
+ self._namespace = namespace
+
+ def Generate(self):
+ """Generates a Code object for features.
+ """
+ c = Code()
+ (c.Append(cpp_util.CHROMIUM_LICENSE)
+ .Append()
+ .Append(cpp_util.GENERATED_FEATURE_MESSAGE % self._source_file)
+ .Append()
+ .Append('#include <string>')
+ .Append()
+ .Append('#include "%s.h"' % self._source_file_filename)
+ .Append()
+ .Append('#include "base/logging.h"')
+ .Append()
+ .Concat(cpp_util.OpenNamespace(self._namespace))
+ .Append()
+ )
+
+ # Generate the constructor.
+ (c.Append('%s::%s() {' % (self._class_name, self._class_name))
+ .Sblock()
+ )
+ for feature in self._feature_defs:
+ c.Append('features_["%s"] = %s;'
+ % (feature.name, cpp_util.ConstantName(feature.name)))
+ (c.Eblock()
+ .Append('}')
+ .Append()
+ )
+
+ # Generate the ToString function.
+ (c.Append('const char* %s::ToString('
+ '%s::ID id) const {' % (self._class_name, self._class_name))
+ .Sblock()
+ .Append('switch (id) {')
+ .Sblock()
+ )
+ for feature in self._feature_defs:
+ c.Append('case %s: return "%s";' %
+ (cpp_util.ConstantName(feature.name), feature.name))
+ (c.Append('case kUnknown: break;')
+ .Append('case kEnumBoundary: break;')
+ .Eblock()
+ .Append('}')
+ .Append('NOTREACHED();')
+ .Append('return "";')
+ )
+ (c.Eblock()
+ .Append('}')
+ .Append()
+ )
+
+ # Generate the FromString function.
+
+ (c.Append('%s::ID %s::FromString('
+ 'const std::string& id) const {'
+ % (self._class_name, self._class_name))
+ .Sblock()
+ .Append('std::map<std::string, %s::ID>::const_iterator it'
+ ' = features_.find(id);' % self._class_name)
+ .Append('if (it == features_.end())')
+ .Append(' return kUnknown;')
+ .Append('return it->second;')
+ .Eblock()
+ .Append('}')
+ .Append()
+ .Cblock(cpp_util.CloseNamespace(self._namespace))
+ )
+
+ return c
diff --git a/chromium/tools/json_schema_compiler/features_compiler.py b/chromium/tools/json_schema_compiler/features_compiler.py
new file mode 100755
index 00000000000..1e4e81ada76
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/features_compiler.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Generator for C++ features from json files.
+
+Usage example:
+ features_compiler.py --destdir gen --root /home/Work/src _permissions.json
+"""
+
+import optparse
+import os
+
+from schema_loader import SchemaLoader
+from features_cc_generator import CCGenerator
+from features_h_generator import HGenerator
+from model import CreateFeature
+
+
+def _GenerateSchema(filename, root, destdir, namespace):
+ """Generates C++ features files from the json file |filename|.
+ """
+ # Load in the feature permissions from the JSON file.
+ schema = os.path.normpath(filename)
+ schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(schema, root)),
+ os.path.dirname(schema))
+ schema_filename = os.path.splitext(schema)[0]
+ feature_defs = schema_loader.LoadSchema(schema)
+
+ # Generate a list of the features defined and a list of their models.
+ feature_list = []
+ for feature_def, feature in feature_defs.iteritems():
+ feature_list.append(CreateFeature(feature_def, feature))
+
+ source_file_dir, _ = os.path.split(schema)
+ relpath = os.path.relpath(os.path.normpath(source_file_dir), root)
+ full_path = os.path.join(relpath, schema)
+
+ generators = [
+ ('%s.cc' % schema_filename, CCGenerator()),
+ ('%s.h' % schema_filename, HGenerator())
+ ]
+
+ # Generate and output the code for all features.
+ output_code = []
+ for filename, generator in generators:
+ code = generator.Generate(feature_list, full_path, namespace).Render()
+ if destdir:
+ with open(os.path.join(destdir, relpath, filename), 'w') as f:
+ f.write(code)
+ output_code += [filename, '', code, '']
+
+ return '\n'.join(output_code)
+
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser(
+ description='Generates a C++ features model from JSON schema',
+ usage='usage: %prog [option]... schema')
+ parser.add_option('-r', '--root', default='.',
+ help='logical include root directory. Path to schema files from '
+ 'specified dir will be the include path.')
+ parser.add_option('-d', '--destdir',
+ help='root directory to output generated files.')
+ parser.add_option('-n', '--namespace', default='generated_features',
+ help='C++ namespace for generated files. e.g extensions::api.')
+ (opts, filenames) = parser.parse_args()
+
+ # Only one file is currently specified.
+ if len(filenames) != 1:
+ raise ValueError('One (and only one) file is required (for now).')
+
+ result = _GenerateSchema(filenames[0], opts.root, opts.destdir,
+ opts.namespace)
+ if not opts.destdir:
+ print result
diff --git a/chromium/tools/json_schema_compiler/features_h_generator.py b/chromium/tools/json_schema_compiler/features_h_generator.py
new file mode 100644
index 00000000000..025f734fd95
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/features_h_generator.py
@@ -0,0 +1,95 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os.path
+
+from code import Code
+import cpp_util
+
+
+class HGenerator(object):
+ def Generate(self, features, source_file, namespace):
+ return _Generator(features, source_file, namespace).Generate()
+
+
+class _Generator(object):
+ """A .cc generator for features.
+ """
+ def __init__(self, features, source_file, namespace):
+ self._feature_defs = features
+ self._source_file = source_file
+ self._source_file_filename, _ = os.path.splitext(source_file)
+ self._class_name = cpp_util.ClassName(self._source_file_filename)
+ self._namespace = namespace
+
+ def Generate(self):
+ """Generates a Code object for features.
+ """
+ c = Code()
+ (c.Append(cpp_util.CHROMIUM_LICENSE)
+ .Append()
+ .Append(cpp_util.GENERATED_FEATURE_MESSAGE % self._source_file)
+ .Append()
+ )
+ ifndef_name = cpp_util.GenerateIfndefName(self._source_file_filename,
+ self._class_name)
+ (c.Append('#ifndef %s' % ifndef_name)
+ .Append('#define %s' % ifndef_name)
+ .Append()
+ )
+
+ (c.Append('#include <map>')
+ .Append('#include <string>')
+ .Append()
+ .Concat(cpp_util.OpenNamespace(self._namespace))
+ .Append()
+ )
+
+ (c.Append('class %s {' % self._class_name)
+ .Append(' public:')
+ .Sblock()
+ .Concat(self._GeneratePublicBody())
+ .Eblock()
+ .Append(' private:')
+ .Sblock()
+ .Concat(self._GeneratePrivateBody())
+ .Eblock('};')
+ .Append()
+ .Cblock(cpp_util.CloseNamespace(self._namespace))
+ )
+ (c.Append('#endif // %s' % ifndef_name)
+ .Append()
+ )
+ return c
+
+ def _GeneratePublicBody(self):
+ c = Code()
+
+ (c.Append('%s();' % self._class_name)
+ .Append()
+ .Append('enum ID {')
+ .Concat(self._GenerateEnumConstants())
+ .Eblock('};')
+ .Append()
+ .Append('const char* ToString(ID id) const;')
+ .Append('ID FromString(const std::string& id) const;')
+ .Append()
+ )
+ return c
+
+ def _GeneratePrivateBody(self):
+ return Code().Append('std::map<std::string, '
+ '%s::ID> features_;' % self._class_name)
+
+ def _GenerateEnumConstants(self):
+ c = Code()
+
+ (c.Sblock()
+ .Append('kUnknown,')
+ )
+ for feature in self._feature_defs:
+ c.Append('%s,' % cpp_util.ConstantName(feature.name))
+ c.Append('kEnumBoundary')
+
+ return c
diff --git a/chromium/tools/json_schema_compiler/idl_schema.py b/chromium/tools/json_schema_compiler/idl_schema.py
index 44a4a55eaec..c6e49bf507b 100644
--- a/chromium/tools/json_schema_compiler/idl_schema.py
+++ b/chromium/tools/json_schema_compiler/idl_schema.py
@@ -100,14 +100,14 @@ class Callspec(object):
return_type = None
if self.node.GetProperty('TYPEREF') not in ('void', None):
return_type = Typeref(self.node.GetProperty('TYPEREF'),
- self.node,
+ self.node.parent,
{'name': self.node.GetName()}).process(callbacks)
# The IDL parser doesn't allow specifying return types as optional.
# Instead we infer any object return values to be optional.
# TODO(asargent): fix the IDL parser to support optional return types.
if return_type.get('type') == 'object' or '$ref' in return_type:
return_type['optional'] = True
- for node in self.node.children:
+ for node in self.node.GetChildren():
parameter = Param(node).process(callbacks)
if parameter['name'] in self.comment:
parameter['description'] = self.comment[parameter['name']]
@@ -139,7 +139,7 @@ class Dictionary(object):
def process(self, callbacks):
properties = OrderedDict()
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'Member':
k, v = Member(node).process(callbacks)
properties[k] = v
@@ -181,7 +181,7 @@ class Member(object):
option_name))
is_function = False
parameter_comments = OrderedDict()
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'Comment':
(parent_comment, parameter_comments) = ProcessComment(node.GetName())
properties['description'] = parent_comment
@@ -223,7 +223,7 @@ class Typeref(object):
properties = self.additional_properties
result = properties
- if self.parent.GetProperty('OPTIONAL', False):
+ if self.parent.GetProperty('OPTIONAL'):
properties['optional'] = True
# The IDL parser denotes array types by adding a child 'Array' node onto
@@ -290,9 +290,15 @@ class Enum(object):
def process(self, callbacks):
enum = []
- for node in self.node.children:
+ for node in self.node.GetChildren():
if node.cls == 'EnumItem':
- enum.append(node.GetName())
+ enum_value = {'name': node.GetName()}
+ for child in node.GetChildren():
+ if child.cls == 'Comment':
+ enum_value['description'] = ProcessComment(child.GetName())[0]
+ else:
+ raise ValueError('Did not process %s %s' % (child.cls, child))
+ enum.append(enum_value)
elif node.cls == 'Comment':
self.description = ProcessComment(node.GetName())[0]
else:
@@ -313,10 +319,18 @@ class Namespace(object):
dictionary that the JSON schema compiler expects to see.
'''
- def __init__(self, namespace_node, description, nodoc=False, internal=False):
+ def __init__(self,
+ namespace_node,
+ description,
+ nodoc=False,
+ internal=False,
+ platforms=None,
+ compiler_options=None):
self.namespace = namespace_node
self.nodoc = nodoc
self.internal = internal
+ self.platforms = platforms
+ self.compiler_options = compiler_options
self.events = []
self.functions = []
self.types = []
@@ -324,7 +338,7 @@ class Namespace(object):
self.description = description
def process(self):
- for node in self.namespace.children:
+ for node in self.namespace.GetChildren():
if node.cls == 'Dictionary':
self.types.append(Dictionary(node).process(self.callbacks))
elif node.cls == 'Callback':
@@ -338,17 +352,23 @@ class Namespace(object):
self.types.append(Enum(node).process(self.callbacks))
else:
sys.exit('Did not process %s %s' % (node.cls, node))
+ if self.compiler_options is not None:
+ compiler_options = self.compiler_options
+ else:
+ compiler_options = {}
return {'namespace': self.namespace.GetName(),
'description': self.description,
'nodoc': self.nodoc,
'types': self.types,
'functions': self.functions,
'internal': self.internal,
- 'events': self.events}
+ 'events': self.events,
+ 'platforms': self.platforms,
+ 'compiler_options': compiler_options}
def process_interface(self, node):
members = []
- for member in node.children:
+ for member in node.GetChildren():
if member.cls == 'Member':
name, properties = Member(member).process(self.callbacks)
members.append(properties)
@@ -369,6 +389,8 @@ class IDLSchema(object):
nodoc = False
internal = False
description = None
+ platforms = None
+ compiler_options = None
for node in self.idl:
if node.cls == 'Namespace':
if not description:
@@ -376,10 +398,14 @@ class IDLSchema(object):
print('%s must have a namespace-level comment. This will '
'appear on the API summary page.' % node.GetName())
description = ''
- namespace = Namespace(node, description, nodoc, internal)
+ namespace = Namespace(node, description, nodoc, internal,
+ platforms=platforms,
+ compiler_options=compiler_options)
namespaces.append(namespace.process())
nodoc = False
internal = False
+ platforms = None
+ compiler_options = None
elif node.cls == 'Copyright':
continue
elif node.cls == 'Comment':
@@ -389,6 +415,10 @@ class IDLSchema(object):
nodoc = bool(node.value)
elif node.name == 'internal':
internal = bool(node.value)
+ elif node.name == 'platforms':
+ platforms = list(node.value)
+ elif node.name == 'implemented_in':
+ compiler_options = {'implemented_in': node.value}
else:
continue
else:
diff --git a/chromium/tools/json_schema_compiler/idl_schema_test.py b/chromium/tools/json_schema_compiler/idl_schema_test.py
index 1f5d7d67ec2..212efdc6713 100755
--- a/chromium/tools/json_schema_compiler/idl_schema_test.py
+++ b/chromium/tools/json_schema_compiler/idl_schema_test.py
@@ -18,6 +18,11 @@ def getParams(schema, name):
return function['parameters']
+def getReturns(schema, name):
+ function = getFunction(schema, name)
+ return function['returns']
+
+
def getType(schema, id):
for item in schema['types']:
if item['id'] == id:
@@ -77,7 +82,9 @@ class IdlSchemaTest(unittest.TestCase):
def testEnum(self):
schema = self.idl_basics
- expected = {'enum': ['name1', 'name2'], 'description': 'Enum description',
+ expected = {'enum': [{'name': 'name1', 'description': 'comment1'},
+ {'name': 'name2'}],
+ 'description': 'Enum description',
'type': 'string', 'id': 'EnumType'}
self.assertEquals(expected, getType(schema, expected['id']))
@@ -108,6 +115,60 @@ class IdlSchemaTest(unittest.TestCase):
self.assertTrue(idl_basics['internal'])
self.assertFalse(idl_basics['nodoc'])
+ def testReturnTypes(self):
+ schema = self.idl_basics
+ self.assertEquals({'name': 'function19', 'type': 'integer'},
+ getReturns(schema, 'function19'))
+ self.assertEquals({'name': 'function20', '$ref': 'MyType1',
+ 'optional': True},
+ getReturns(schema, 'function20'))
+ self.assertEquals({'name': 'function21', 'type': 'array',
+ 'items': {'$ref': 'MyType1'}},
+ getReturns(schema, 'function21'))
+ self.assertEquals({'name': 'function22', '$ref': 'EnumType',
+ 'optional': True},
+ getReturns(schema, 'function22'))
+ self.assertEquals({'name': 'function23', 'type': 'array',
+ 'items': {'$ref': 'EnumType'}},
+ getReturns(schema, 'function23'))
+
+ def testChromeOSPlatformsNamespace(self):
+ schema = idl_schema.Load('test/idl_namespace_chromeos.idl')[0]
+ self.assertEquals('idl_namespace_chromeos', schema['namespace'])
+ expected = ['chromeos']
+ self.assertEquals(expected, schema['platforms'])
+
+ def testAllPlatformsNamespace(self):
+ schema = idl_schema.Load('test/idl_namespace_all_platforms.idl')[0]
+ self.assertEquals('idl_namespace_all_platforms', schema['namespace'])
+ expected = ['chromeos', 'chromeos_touch', 'linux', 'mac', 'win']
+ self.assertEquals(expected, schema['platforms'])
+
+ def testNonSpecificPlatformsNamespace(self):
+ schema = idl_schema.Load('test/idl_namespace_non_specific_platforms.idl')[0]
+ self.assertEquals('idl_namespace_non_specific_platforms',
+ schema['namespace'])
+ expected = None
+ self.assertEquals(expected, schema['platforms'])
+
+ def testSpecificImplementNamespace(self):
+ schema = idl_schema.Load('test/idl_namespace_specific_implement.idl')[0]
+ self.assertEquals('idl_namespace_specific_implement',
+ schema['namespace'])
+ expected = 'idl_namespace_specific_implement.idl'
+ self.assertEquals(expected, schema['compiler_options']['implemented_in'])
+
+ def testSpecificImplementOnChromeOSNamespace(self):
+ schema = idl_schema.Load(
+ 'test/idl_namespace_specific_implement_chromeos.idl')[0]
+ self.assertEquals('idl_namespace_specific_implement_chromeos',
+ schema['namespace'])
+ expected_implemented_path = 'idl_namespace_specific_implement_chromeos.idl'
+ expected_platform = ['chromeos']
+ self.assertEquals(expected_implemented_path,
+ schema['compiler_options']['implemented_in'])
+ self.assertEquals(expected_platform, schema['platforms'])
+
def testCallbackComment(self):
schema = self.idl_basics
self.assertEquals('A comment on a callback.',
@@ -143,10 +204,12 @@ class IdlSchemaTest(unittest.TestCase):
schema = idl_schema.Load('test/idl_reserved_words.idl')[0]
foo_type = getType(schema, 'Foo')
- self.assertEquals(['float', 'DOMString'], foo_type['enum'])
+ self.assertEquals([{'name': 'float'}, {'name': 'DOMString'}],
+ foo_type['enum'])
enum_type = getType(schema, 'enum')
- self.assertEquals(['callback', 'namespace'], enum_type['enum'])
+ self.assertEquals([{'name': 'callback'}, {'name': 'namespace'}],
+ enum_type['enum'])
dictionary = getType(schema, 'dictionary')
self.assertEquals('integer', dictionary['properties']['long']['type'])
diff --git a/chromium/tools/json_schema_compiler/memoize.py b/chromium/tools/json_schema_compiler/memoize.py
index 1402a6ecd80..228e7e3f82d 100644
--- a/chromium/tools/json_schema_compiler/memoize.py
+++ b/chromium/tools/json_schema_compiler/memoize.py
@@ -6,8 +6,9 @@ def memoize(fn):
'''Decorates |fn| to memoize.
'''
memory = {}
- def impl(*args):
- if args not in memory:
- memory[args] = fn(*args)
- return memory[args]
+ def impl(*args, **optargs):
+ full_args = args + tuple(optargs.iteritems())
+ if full_args not in memory:
+ memory[full_args] = fn(*args, **optargs)
+ return memory[full_args]
return impl
diff --git a/chromium/tools/json_schema_compiler/model.py b/chromium/tools/json_schema_compiler/model.py
index 082fbc60f85..dd68e9b1056 100644
--- a/chromium/tools/json_schema_compiler/model.py
+++ b/chromium/tools/json_schema_compiler/model.py
@@ -7,6 +7,7 @@ import os.path
from json_parse import OrderedDict
from memoize import memoize
+
class ParseException(Exception):
"""Thrown when data in the model is invalid.
"""
@@ -16,6 +17,7 @@ class ParseException(Exception):
Exception.__init__(
self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
+
class Model(object):
"""Model of all namespaces that comprise an API.
@@ -34,6 +36,45 @@ class Model(object):
self.namespaces[namespace.name] = namespace
return namespace
+
+def CreateFeature(name, model):
+ if isinstance(model, dict):
+ return SimpleFeature(name, model)
+ return ComplexFeature(name, [SimpleFeature(name, child) for child in model])
+
+
+class ComplexFeature(object):
+ """A complex feature which may be made of several simple features.
+
+ Properties:
+ - |name| the name of the feature
+ - |unix_name| the unix_name of the feature
+ - |feature_list| a list of simple features which make up the feature
+ """
+ def __init__(self, feature_name, features):
+ self.name = feature_name
+ self.unix_name = UnixName(self.name)
+ self.feature_list = features
+
+class SimpleFeature(object):
+ """A simple feature, which can make up a complex feature, as specified in
+ files such as chrome/common/extensions/api/_permission_features.json.
+
+ Properties:
+ - |name| the name of the feature
+ - |unix_name| the unix_name of the feature
+ - |channel| the channel where the feature is released
+ - |extension_types| the types which can use the feature
+ - |whitelist| a list of extensions allowed to use the feature
+ """
+ def __init__(self, feature_name, feature_def):
+ self.name = feature_name
+ self.unix_name = UnixName(self.name)
+ self.channel = feature_def['channel']
+ self.extension_types = feature_def['extension_types']
+ self.whitelist = feature_def.get('whitelist')
+
+
class Namespace(object):
"""An API namespace.
@@ -71,8 +112,12 @@ class Namespace(object):
self.functions = _GetFunctions(self, json, self)
self.events = _GetEvents(self, json, self)
self.properties = _GetProperties(self, json, self, toplevel_origin)
- self.compiler_options = (json.get('compiler_options', {})
- if include_compiler_options else {})
+ if include_compiler_options:
+ self.compiler_options = json.get('compiler_options', {})
+ else:
+ self.compiler_options = {}
+ self.documentation_options = json.get('documentation_options', {})
+
class Origin(object):
"""Stores the possible origin of model object as a pair of bools. These are:
@@ -92,6 +137,7 @@ class Origin(object):
self.from_client = from_client
self.from_json = from_json
+
class Type(object):
"""A Type defined in the json.
@@ -140,7 +186,7 @@ class Type(object):
self.ref_type = json['$ref']
elif 'enum' in json and json_type == 'string':
self.property_type = PropertyType.ENUM
- self.enum_values = [value for value in json['enum']]
+ self.enum_values = [EnumValue(value) for value in json['enum']]
elif json_type == 'any':
self.property_type = PropertyType.ANY
elif json_type == 'binary':
@@ -198,6 +244,7 @@ class Type(object):
else:
raise ParseException(self, 'Unsupported JSON type %s' % json_type)
+
class Function(object):
"""A Function defined in the API.
@@ -207,6 +254,7 @@ class Function(object):
available to
- |params| a list of parameters to the function (order matters). A separate
parameter is used for each choice of a 'choices' parameter
+ - |deprecated| a reason and possible alternative for a deprecated function
- |description| a description of the function (if provided)
- |callback| the callback parameter to the function. There should be exactly
one
@@ -227,6 +275,7 @@ class Function(object):
self.platforms = _GetPlatforms(json)
self.params = []
self.description = json.get('description')
+ self.deprecated = json.get('deprecated')
self.callback = None
self.optional = json.get('optional', False)
self.parent = parent
@@ -236,6 +285,7 @@ class Function(object):
self.actions = options.get('actions', [])
self.supports_listeners = options.get('supportsListeners', True)
self.supports_rules = options.get('supportsRules', False)
+ self.supports_dom = options.get('supportsDom', False)
def GeneratePropertyFromParam(p):
return Property(self, p['name'], p, namespace, origin)
@@ -268,6 +318,7 @@ class Function(object):
namespace,
origin)
+
class Property(object):
"""A property of a type OR a parameter to a function.
Properties:
@@ -278,6 +329,7 @@ class Property(object):
- |description| a description of the property (if provided)
- |type_| the model.Type of this property
- |simple_name| the name of this Property without a namespace
+ - |deprecated| a reason and possible alternative for a deprecated property
"""
def __init__(self, parent, name, json, namespace, origin):
"""Creates a Property from JSON.
@@ -291,6 +343,7 @@ class Property(object):
self.description = json.get('description', None)
self.optional = json.get('optional', None)
self.instance_of = json.get('isInstanceOf', None)
+ self.deprecated = json.get('deprecated')
# HACK: only support very specific value types.
is_allowed_value = (
@@ -339,6 +392,20 @@ class Property(object):
unix_name = property(GetUnixName, SetUnixName)
+class EnumValue(object):
+ """A single value from an enum.
+ Properties:
+ - |name| name of the property as in the json.
+ - |description| a description of the property (if provided)
+ """
+ def __init__(self, json):
+ if isinstance(json, dict):
+ self.name = json['name']
+ self.description = json.get('description')
+ else:
+ self.name = json
+ self.description = None
+
class _Enum(object):
"""Superclass for enum types with a "name" field, setting up repr/eq/ne.
Enums need to do this so that equality/non-equality work over pickling.
@@ -366,11 +433,13 @@ class _Enum(object):
def __str__(self):
return repr(self)
+
class _PropertyTypeInfo(_Enum):
def __init__(self, is_fundamental, name):
_Enum.__init__(self, name)
self.is_fundamental = is_fundamental
+
class PropertyType(object):
"""Enum of different types of properties/parameters.
"""
@@ -388,6 +457,7 @@ class PropertyType(object):
REF = _PropertyTypeInfo(False, "ref")
STRING = _PropertyTypeInfo(True, "string")
+
@memoize
def UnixName(name):
'''Returns the unix_style name for a given lowerCamelCase string.
@@ -409,11 +479,13 @@ def UnixName(name):
unix_name.append(c.lower())
return ''.join(unix_name)
+
def _StripNamespace(name, namespace):
if name.startswith(namespace.name + '.'):
return name[len(namespace.name + '.'):]
return name
+
def _GetModelHierarchy(entity):
"""Returns the hierarchy of the given model entity."""
hierarchy = []
@@ -425,6 +497,7 @@ def _GetModelHierarchy(entity):
hierarchy.reverse()
return hierarchy
+
def _GetTypes(parent, json, namespace, origin):
"""Creates Type objects extracted from |json|.
"""
@@ -434,6 +507,7 @@ def _GetTypes(parent, json, namespace, origin):
types[type_.name] = type_
return types
+
def _GetFunctions(parent, json, namespace):
"""Creates Function objects extracted from |json|.
"""
@@ -447,6 +521,7 @@ def _GetFunctions(parent, json, namespace):
functions[function.name] = function
return functions
+
def _GetEvents(parent, json, namespace):
"""Creates Function objects generated from the events in |json|.
"""
@@ -460,6 +535,7 @@ def _GetEvents(parent, json, namespace):
events[event.name] = event
return events
+
def _GetProperties(parent, json, namespace, origin):
"""Generates Property objects extracted from |json|.
"""
@@ -468,10 +544,12 @@ def _GetProperties(parent, json, namespace, origin):
properties[name] = Property(parent, name, property_json, namespace, origin)
return properties
+
class _PlatformInfo(_Enum):
def __init__(self, name):
_Enum.__init__(self, name)
+
class Platforms(object):
"""Enum of the possible platforms.
"""
@@ -481,9 +559,13 @@ class Platforms(object):
MAC = _PlatformInfo("mac")
WIN = _PlatformInfo("win")
+
def _GetPlatforms(json):
- if 'platforms' not in json:
+ if 'platforms' not in json or json['platforms'] == None:
return None
+ # Sanity check: platforms should not be an empty list.
+ if not json['platforms']:
+ raise ValueError('"platforms" cannot be an empty list')
platforms = []
for platform_name in json['platforms']:
for platform_enum in _Enum.GetAll(Platforms):
diff --git a/chromium/tools/json_schema_compiler/model_test.py b/chromium/tools/json_schema_compiler/model_test.py
index bdd20092810..0e398a5a5bb 100755
--- a/chromium/tools/json_schema_compiler/model_test.py
+++ b/chromium/tools/json_schema_compiler/model_test.py
@@ -4,6 +4,8 @@
# found in the LICENSE file.
from json_schema import CachedLoad
+from idl_schema import Load
+from model import Platforms
import model
import unittest
@@ -22,9 +24,25 @@ class ModelTest(unittest.TestCase):
self.model.AddNamespace(self.tabs_json[0],
'path/to/tabs.json')
self.tabs = self.model.namespaces.get('tabs')
+ self.idl_chromeos = Load('test/idl_namespace_chromeos.idl')
+ self.model.AddNamespace(self.idl_chromeos[0],
+ 'path/to/idl_namespace_chromeos.idl')
+ self.idl_namespace_chromeos = self.model.namespaces.get(
+ 'idl_namespace_chromeos')
+ self.idl_all_platforms = Load('test/idl_namespace_all_platforms.idl')
+ self.model.AddNamespace(self.idl_all_platforms[0],
+ 'path/to/idl_namespace_all_platforms.idl')
+ self.idl_namespace_all_platforms = self.model.namespaces.get(
+ 'idl_namespace_all_platforms')
+ self.idl_non_specific_platforms = Load(
+ 'test/idl_namespace_non_specific_platforms.idl')
+ self.model.AddNamespace(self.idl_non_specific_platforms[0],
+ 'path/to/idl_namespace_non_specific_platforms.idl')
+ self.idl_namespace_non_specific_platforms = self.model.namespaces.get(
+ 'idl_namespace_non_specific_platforms')
def testNamespaces(self):
- self.assertEquals(3, len(self.model.namespaces))
+ self.assertEquals(6, len(self.model.namespaces))
self.assertTrue(self.permissions)
def testHasFunctions(self):
@@ -100,6 +118,15 @@ class ModelTest(unittest.TestCase):
for name in expectations:
self.assertEquals(expectations[name], model.UnixName(name))
+ def testPlatforms(self):
+ self.assertEqual([Platforms.CHROMEOS],
+ self.idl_namespace_chromeos.platforms)
+ self.assertEqual(
+ [Platforms.CHROMEOS, Platforms.CHROMEOS_TOUCH, Platforms.LINUX,
+ Platforms.MAC, Platforms.WIN],
+ self.idl_namespace_all_platforms.platforms)
+ self.assertEqual(None,
+ self.idl_namespace_non_specific_platforms.platforms)
if __name__ == '__main__':
unittest.main()
diff --git a/chromium/tools/json_schema_compiler/ppapi_generator.py b/chromium/tools/json_schema_compiler/ppapi_generator.py
new file mode 100644
index 00000000000..e47b9540e34
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/ppapi_generator.py
@@ -0,0 +1,289 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import datetime
+import os.path
+import sys
+
+import code
+import cpp_util
+import model
+
+try:
+ import jinja2
+except ImportError:
+ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..',
+ 'third_party'))
+ import jinja2
+
+
+class _PpapiGeneratorBase(object):
+ """A base class for ppapi generators.
+
+ Implementations should set TEMPLATE_NAME to a string containing the name of
+ the template file without its extension. The template will be rendered with
+ the following symbols available:
+ name: A string containing the name of the namespace.
+ enums: A list of enums within the namespace.
+ types: A list of types within the namespace, sorted such that no element
+ depends on an earlier element.
+ events: A dict of events within the namespace.
+ functions: A dict of functions within the namespace.
+ year: An int containing the current year.
+ source_file: The name of the input file.
+ """
+
+ def __init__(self, namespace):
+ self._namespace = namespace
+ self._required_types = {}
+ self._array_types = set()
+ self._optional_types = set()
+ self._optional_array_types = set()
+ self._dependencies = collections.OrderedDict()
+ self._types = []
+ self._enums = []
+
+ self.jinja_environment = jinja2.Environment(
+ loader=jinja2.FileSystemLoader(os.path.join(os.path.dirname(__file__),
+ 'templates', 'ppapi')))
+ self._SetupFilters()
+ self._ResolveTypeDependencies()
+
+ def _SetupFilters(self):
+ self.jinja_environment.filters.update({
+ 'ppapi_type': self.ToPpapiType,
+ 'classname': cpp_util.Classname,
+ 'enum_value': self.EnumValueName,
+ 'return_type': self.GetFunctionReturnType,
+ 'format_param_type': self.FormatParamType,
+ 'needs_optional': self.NeedsOptional,
+ 'needs_array': self.NeedsArray,
+ 'needs_optional_array': self.NeedsOptionalArray,
+ 'has_array_outs': self.HasArrayOuts,
+ })
+
+ def Render(self, template_name, values):
+ generated_code = code.Code()
+ template = self.jinja_environment.get_template(
+ '%s.template' % template_name)
+ generated_code.Append(template.render(values))
+ return generated_code
+
+ def Generate(self):
+ """Generates a Code object for a single namespace."""
+ return self.Render(self.TEMPLATE_NAME, {
+ 'name': self._namespace.name,
+ 'enums': self._enums,
+ 'types': self._types,
+ 'events': self._namespace.events,
+ 'functions': self._namespace.functions,
+ # TODO(sammc): Don't change years when regenerating existing output files.
+ 'year': datetime.date.today().year,
+ 'source_file': self._namespace.source_file,
+ })
+
+ def _ResolveTypeDependencies(self):
+ """Calculates the transitive closure of the types in _required_types.
+
+ Returns a tuple containing the list of struct types and the list of enum
+ types. The list of struct types is ordered such that no type depends on a
+ type later in the list.
+
+ """
+ if self._namespace.functions:
+ for function in self._namespace.functions.itervalues():
+ self._FindFunctionDependencies(function)
+
+ if self._namespace.events:
+ for event in self._namespace.events.itervalues():
+ self._FindFunctionDependencies(event)
+ resolved_types = set()
+ while resolved_types < set(self._required_types):
+ for typename in sorted(set(self._required_types) - resolved_types):
+ type_ = self._required_types[typename]
+ self._dependencies.setdefault(typename, set())
+ for member in type_.properties.itervalues():
+ self._RegisterDependency(member, self._NameComponents(type_))
+ resolved_types.add(typename)
+ while self._dependencies:
+ for name, deps in self._dependencies.items():
+ if not deps:
+ if (self._required_types[name].property_type ==
+ model.PropertyType.ENUM):
+ self._enums.append(self._required_types[name])
+ else:
+ self._types.append(self._required_types[name])
+ for deps in self._dependencies.itervalues():
+ deps.discard(name)
+ del self._dependencies[name]
+ break
+ else:
+ raise ValueError('Circular dependency %s' % self._dependencies)
+
+ def _FindFunctionDependencies(self, function):
+ for param in function.params:
+ self._RegisterDependency(param, None)
+ if function.callback:
+ for param in function.callback.params:
+ self._RegisterDependency(param, None)
+ if function.returns:
+ self._RegisterTypeDependency(function.returns, None, False, False)
+
+ def _RegisterDependency(self, member, depender):
+ self._RegisterTypeDependency(member.type_, depender, member.optional, False)
+
+ def _RegisterTypeDependency(self, type_, depender, optional, array):
+ if type_.property_type == model.PropertyType.ARRAY:
+ self._RegisterTypeDependency(type_.item_type, depender, optional, True)
+ elif type_.property_type == model.PropertyType.REF:
+ self._RegisterTypeDependency(self._namespace.types[type_.ref_type],
+ depender, optional, array)
+ elif type_.property_type in (model.PropertyType.OBJECT,
+ model.PropertyType.ENUM):
+ name_components = self._NameComponents(type_)
+ self._required_types[name_components] = type_
+ if depender:
+ self._dependencies.setdefault(depender, set()).add(
+ name_components)
+ if array:
+ self._array_types.add(name_components)
+ if optional:
+ self._optional_array_types.add(name_components)
+ elif optional:
+ self._optional_types.add(name_components)
+
+ @staticmethod
+ def _NameComponents(entity):
+ """Returns a tuple of the fully-qualified name of an entity."""
+ names = []
+ while entity:
+ if (not isinstance(entity, model.Type) or
+ entity.property_type != model.PropertyType.ARRAY):
+ names.append(entity.name)
+ entity = entity.parent
+ return tuple(reversed(names[:-1]))
+
+ def ToPpapiType(self, type_, array=False, optional=False):
+ """Returns a string containing the name of the Pepper C type for |type_|.
+
+ If array is True, returns the name of an array of |type_|. If optional is
+ True, returns the name of an optional |type_|. If both array and optional
+ are True, returns the name of an optional array of |type_|.
+ """
+ if isinstance(type_, model.Function) or type_.property_type in (
+ model.PropertyType.OBJECT, model.PropertyType.ENUM):
+ return self._FormatPpapiTypeName(
+ array, optional, '_'.join(
+ cpp_util.Classname(s) for s in self._NameComponents(type_)),
+ namespace=cpp_util.Classname(self._namespace.name))
+ elif type_.property_type == model.PropertyType.REF:
+ return self.ToPpapiType(self._namespace.types[type_.ref_type],
+ optional=optional, array=array)
+ elif type_.property_type == model.PropertyType.ARRAY:
+ return self.ToPpapiType(type_.item_type, array=True,
+ optional=optional)
+ elif type_.property_type == model.PropertyType.STRING and not array:
+ return 'PP_Var'
+ elif array or optional:
+ if type_.property_type in self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP:
+ return self._FormatPpapiTypeName(
+ array, optional,
+ self._PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP[type_.property_type], '')
+ return self._PPAPI_PRIMITIVE_TYPE_MAP.get(type_.property_type, 'PP_Var')
+
+ _PPAPI_PRIMITIVE_TYPE_MAP = {
+ model.PropertyType.BOOLEAN: 'PP_Bool',
+ model.PropertyType.DOUBLE: 'double_t',
+ model.PropertyType.INT64: 'int64_t',
+ model.PropertyType.INTEGER: 'int32_t',
+ }
+ _PPAPI_COMPOUND_PRIMITIVE_TYPE_MAP = {
+ model.PropertyType.BOOLEAN: 'Bool',
+ model.PropertyType.DOUBLE: 'Double',
+ model.PropertyType.INT64: 'Int64',
+ model.PropertyType.INTEGER: 'Int32',
+ model.PropertyType.STRING: 'String',
+ }
+
+ @staticmethod
+ def _FormatPpapiTypeName(array, optional, name, namespace=''):
+ if namespace:
+ namespace = '%s_' % namespace
+ if array:
+ if optional:
+ return 'PP_%sOptional_%s_Array' % (namespace, name)
+ return 'PP_%s%s_Array' % (namespace, name)
+ if optional:
+ return 'PP_%sOptional_%s' % (namespace, name)
+ return 'PP_%s%s' % (namespace, name)
+
+ def NeedsOptional(self, type_):
+ """Returns True if an optional |type_| is required."""
+ return self._NameComponents(type_) in self._optional_types
+
+ def NeedsArray(self, type_):
+ """Returns True if an array of |type_| is required."""
+ return self._NameComponents(type_) in self._array_types
+
+ def NeedsOptionalArray(self, type_):
+ """Returns True if an optional array of |type_| is required."""
+ return self._NameComponents(type_) in self._optional_array_types
+
+ def FormatParamType(self, param):
+ """Formats the type of a parameter or property."""
+ return self.ToPpapiType(param.type_, optional=param.optional)
+
+ @staticmethod
+ def GetFunctionReturnType(function):
+ return 'int32_t' if function.callback or function.returns else 'void'
+
+ def EnumValueName(self, enum_value, enum_type):
+ """Returns a string containing the name for an enum value."""
+ return '%s_%s' % (self.ToPpapiType(enum_type).upper(),
+ enum_value.name.upper())
+
+ def _ResolveType(self, type_):
+ if type_.property_type == model.PropertyType.REF:
+ return self._ResolveType(self._namespace.types[type_.ref_type])
+ if type_.property_type == model.PropertyType.ARRAY:
+ return self._ResolveType(type_.item_type)
+ return type_
+
+ def _IsOrContainsArray(self, type_):
+ if type_.property_type == model.PropertyType.ARRAY:
+ return True
+ type_ = self._ResolveType(type_)
+ if type_.property_type == model.PropertyType.OBJECT:
+ return any(self._IsOrContainsArray(param.type_)
+ for param in type_.properties.itervalues())
+ return False
+
+ def HasArrayOuts(self, function):
+ """Returns True if the function produces any arrays as outputs.
+
+ This includes arrays that are properties of other objects.
+ """
+ if function.callback:
+ for param in function.callback.params:
+ if self._IsOrContainsArray(param.type_):
+ return True
+ return function.returns and self._IsOrContainsArray(function.returns)
+
+
+class _IdlGenerator(_PpapiGeneratorBase):
+ TEMPLATE_NAME = 'idl'
+
+
+class _GeneratorWrapper(object):
+ def __init__(self, generator_factory):
+ self._generator_factory = generator_factory
+
+ def Generate(self, namespace):
+ return self._generator_factory(namespace).Generate()
+
+
+class PpapiGenerator(object):
+ def __init__(self):
+ self.idl_generator = _GeneratorWrapper(_IdlGenerator)
diff --git a/chromium/tools/json_schema_compiler/ppapi_generator_test.py b/chromium/tools/json_schema_compiler/ppapi_generator_test.py
new file mode 100755
index 00000000000..4d396ffa91e
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/ppapi_generator_test.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from model import Model
+import ppapi_generator
+from schema_loader import SchemaLoader
+
+
+def _LoadNamespace(filename):
+ filename = os.path.join(os.path.dirname(__file__), filename)
+ schema_loader = SchemaLoader(
+ os.path.dirname(os.path.relpath(os.path.normpath(filename),
+ os.path.dirname(filename))),
+ os.path.dirname(filename))
+ schema = os.path.normpath(filename)
+ api_def = schema_loader.LoadSchema(os.path.split(schema)[1])[0]
+
+ api_model = Model()
+ relpath = os.path.relpath(os.path.normpath(filename),
+ os.path.dirname(filename))
+ namespace = api_model.AddNamespace(api_def, relpath)
+ generator = ppapi_generator._PpapiGeneratorBase(namespace)
+ return namespace, generator
+
+
+class PpapiGeneratorIdlStructsTest(unittest.TestCase):
+ def setUp(self):
+ self.namespace, self.generator = _LoadNamespace(
+ os.path.join('test', 'idl_pepper.idl'))
+
+ def testTypesOrder(self):
+ typename_to_index = dict(
+ (type_.name, i) for i, type_ in enumerate(self.generator._types))
+ self.assertLess(typename_to_index['MyType1'], typename_to_index['MyType2'])
+ self.assertLess(typename_to_index['MyType1'], typename_to_index['MyType3'])
+ self.assertLess(typename_to_index['MyType1'], typename_to_index['MyType4'])
+ self.assertLess(typename_to_index['MyType1'], typename_to_index['MyType5'])
+ self.assertLess(typename_to_index['MyType3'], typename_to_index['MyType5'])
+ self.assertLess(typename_to_index['MyType4'], typename_to_index['MyType5'])
+ self.assertLess(typename_to_index['MyType5'], typename_to_index['MyType6'])
+
+ def testNeedsArray(self):
+ self.assertFalse(self.generator.NeedsArray(self.namespace.types['MyType1']))
+ self.assertTrue(self.generator.NeedsArray(self.namespace.types['MyType2']))
+ self.assertTrue(self.generator.NeedsArray(self.namespace.types['MyType3']))
+ self.assertFalse(self.generator.NeedsArray(self.namespace.types['MyType4']))
+ self.assertFalse(self.generator.NeedsArray(self.namespace.types['MyType5']))
+ self.assertTrue(self.generator.NeedsArray(self.namespace.types['EnumType']))
+
+ def testNeedsOptional(self):
+ self.assertFalse(
+ self.generator.NeedsOptional(self.namespace.types['MyType1']))
+ self.assertFalse(
+ self.generator.NeedsOptional(self.namespace.types['MyType2']))
+ self.assertTrue(
+ self.generator.NeedsOptional(self.namespace.types['MyType3']))
+ self.assertTrue(
+ self.generator.NeedsOptional(self.namespace.types['MyType4']))
+ self.assertFalse(
+ self.generator.NeedsOptional(self.namespace.types['MyType5']))
+ self.assertTrue(
+ self.generator.NeedsOptional(self.namespace.types['EnumType']))
+
+ def testNeedsOptionalArray(self):
+ self.assertFalse(
+ self.generator.NeedsOptionalArray(self.namespace.types['MyType1']))
+ self.assertTrue(
+ self.generator.NeedsOptionalArray(self.namespace.types['MyType2']))
+ self.assertFalse(
+ self.generator.NeedsOptionalArray(self.namespace.types['MyType3']))
+ self.assertFalse(
+ self.generator.NeedsOptionalArray(self.namespace.types['MyType4']))
+ self.assertFalse(
+ self.generator.NeedsOptionalArray(self.namespace.types['MyType5']))
+ self.assertTrue(
+ self.generator.NeedsOptionalArray(self.namespace.types['EnumType']))
+
+ def testFormatParamTypePrimitive(self):
+ self.assertEqual('int32_t', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['int_single']))
+ self.assertEqual('PP_Int32_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['int_array']))
+ self.assertEqual('PP_Optional_Int32', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_int']))
+ self.assertEqual('PP_Optional_Int32_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_int_array']))
+ self.assertEqual('double_t', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['double_single']))
+ self.assertEqual('PP_Double_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['double_array']))
+ self.assertEqual('PP_Optional_Double', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_double']))
+ self.assertEqual('PP_Optional_Double_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_double_array']))
+ self.assertEqual('PP_Var', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['string']))
+ self.assertEqual('PP_String_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['string_array']))
+ self.assertEqual('PP_Var', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_string']))
+ self.assertEqual('PP_Optional_String_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_string_array']))
+
+ def testFormatParamTypeStruct(self):
+ self.assertEqual('PP_IdlPepper_MyType0', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['struct_single']))
+ self.assertEqual(
+ 'PP_IdlPepper_MyType0_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['struct_array']))
+ self.assertEqual(
+ 'PP_IdlPepper_Optional_MyType0', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_struct']))
+ self.assertEqual(
+ 'PP_IdlPepper_Optional_MyType0_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties[
+ 'optional_struct_array']))
+
+ def testFormatParamTypeEnum(self):
+ self.assertEqual('PP_IdlPepper_EnumType', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['enum_single']))
+ self.assertEqual(
+ 'PP_IdlPepper_EnumType_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['enum_array']))
+ self.assertEqual(
+ 'PP_IdlPepper_Optional_EnumType', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_enum']))
+ self.assertEqual(
+ 'PP_IdlPepper_Optional_EnumType_Array', self.generator.FormatParamType(
+ self.namespace.types['MyType'].properties['optional_enum_array']))
+
+ def testEnumValueName(self):
+ enum_type = self.namespace.types['EnumType']
+ self.assertEqual('PP_IDLPEPPER_ENUMTYPE_NAME1',
+ self.generator.EnumValueName(enum_type.enum_values[0],
+ enum_type))
+ self.assertEqual('PP_IDLPEPPER_ENUMTYPE_NAME2',
+ self.generator.EnumValueName(enum_type.enum_values[1],
+ enum_type))
+ enum_type = self.namespace.types['AnotherEnumType']
+ self.assertEqual('PP_IDLPEPPER_ANOTHERENUMTYPE_NAME1',
+ self.generator.EnumValueName(enum_type.enum_values[0],
+ enum_type))
+ self.assertEqual('PP_IDLPEPPER_ANOTHERENUMTYPE_NAME2',
+ self.generator.EnumValueName(enum_type.enum_values[1],
+ enum_type))
+
+ def testReturnTypes(self):
+ self.assertEqual('void', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function1']))
+ self.assertEqual('void', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function2']))
+ self.assertEqual('int32_t', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function3']))
+ self.assertEqual('int32_t', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function4']))
+ self.assertEqual('int32_t', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function5']))
+ self.assertEqual('int32_t', self.generator.GetFunctionReturnType(
+ self.namespace.functions['function6']))
+
+ def testHasOutArray(self):
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function1']))
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function2']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function3']))
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function4']))
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function5']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function6']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function7']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function8']))
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function9']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function10']))
+ self.assertTrue(self.generator.HasArrayOuts(
+ self.namespace.functions['function11']))
+ self.assertFalse(self.generator.HasArrayOuts(
+ self.namespace.functions['function12']))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/tools/json_schema_compiler/schema_loader.py b/chromium/tools/json_schema_compiler/schema_loader.py
index c434dc16746..9fea1ab8250 100644
--- a/chromium/tools/json_schema_compiler/schema_loader.py
+++ b/chromium/tools/json_schema_compiler/schema_loader.py
@@ -11,9 +11,16 @@ from model import Model
class SchemaLoader(object):
'''Resolves a type name into the namespace the type belongs to.
+
+ Properties:
+ - |display_path| path to the directory with the API header files, intended for
+ use with the Model.
+ - |real_path| path to the directory with the API header files, used for file
+ access.
'''
- def __init__(self, api_path):
- self._api_path = api_path
+ def __init__(self, display_path, real_path):
+ self._display_path = display_path
+ self._real_path = real_path
def ResolveType(self, full_name, default_namespace):
name_parts = full_name.rsplit('.', 1)
@@ -25,29 +32,32 @@ class SchemaLoader(object):
real_name = None
for ext in ['json', 'idl']:
filename = '%s.%s' % (namespace_name, ext)
- if os.path.exists(filename):
+ filepath = os.path.join(self._real_path, filename);
+ if os.path.exists(filepath):
real_name = filename
break
if real_name is None:
return None
- namespace = Model().AddNamespace(self.LoadSchema(real_name)[0],
- os.path.join(self._api_path, real_name))
+ namespace = Model().AddNamespace(
+ self.LoadSchema(real_name)[0],
+ os.path.join(self._display_path, real_name))
if type_name not in namespace.types:
return None
return namespace
def LoadSchema(self, schema):
+ '''Load a schema definition. The schema parameter must be a file name
+ without any path component - the file is loaded from the path defined by
+ the real_path argument passed to the constructor.'''
schema_filename, schema_extension = os.path.splitext(schema)
+ schema_path = os.path.join(self._real_path, schema);
if schema_extension == '.json':
- api_defs = json_schema.Load(schema)
+ api_defs = json_schema.Load(schema_path)
elif schema_extension == '.idl':
- api_defs = idl_schema.Load(schema)
+ api_defs = idl_schema.Load(schema_path)
else:
sys.exit('Did not recognize file extension %s for schema %s' %
(schema_extension, schema))
- if len(api_defs) != 1:
- sys.exit('File %s has multiple schemas. Files are only allowed to contain'
- 'a single schema.' % schema)
return api_defs
diff --git a/chromium/tools/json_schema_compiler/templates/ppapi/base.template b/chromium/tools/json_schema_compiler/templates/ppapi/base.template
new file mode 100644
index 00000000000..df1aa8ef889
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/templates/ppapi/base.template
@@ -0,0 +1,14 @@
+{# Copyright 2013 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -#}
+
+// Copyright {{ year }} The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// GENERATED FROM THE API DEFINITION IN
+// {{ source_file }}
+// DO NOT EDIT.
+
+{% block content %}
+{% endblock %}
diff --git a/chromium/tools/json_schema_compiler/templates/ppapi/idl.template b/chromium/tools/json_schema_compiler/templates/ppapi/idl.template
new file mode 100644
index 00000000000..9c5b998f97f
--- /dev/null
+++ b/chromium/tools/json_schema_compiler/templates/ppapi/idl.template
@@ -0,0 +1,101 @@
+{# Copyright 2013 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file. -#}
+
+{% extends "base.template" %}
+
+{% macro optional_array_struct(type) %}
+{%- if type | needs_optional_array %}
+struct {{ type | ppapi_type(array=True, optional=True) }} {
+ {{ type | ppapi_type(array=True) }} value;
+ PP_Bool is_set;
+};
+{% endif -%}
+{% endmacro %}
+
+{% macro array_struct(type) %}
+{%- if type | needs_array %}
+struct {{ type | ppapi_type(array=True) }} {
+ uint32_t size;
+ [size_is(size)] {{ type | ppapi_type }}[] elements;
+};
+{% endif -%}
+{% endmacro %}
+
+{% macro optional_struct(type) %}
+{%- if type | needs_optional %}
+struct {{ type | ppapi_type(optional=True) }} {
+ {{ type | ppapi_type }} value;
+ PP_Bool is_set;
+};
+{% endif -%}
+{% endmacro %}
+
+{% block content -%}
+{# TODO(sammc): Generate version information. -#}
+label Chrome {
+ [channel=dev] M33 = 0.1
+};
+{% for type in enums %}
+enum {{ type | ppapi_type }} {
+ {%- for value in type.enum_values %}
+ {{ value | enum_value(type) }}{% if not loop.last %},{% endif %}
+ {%- endfor %}
+};
+{{ optional_struct(type) -}}
+{{ array_struct(type) -}}
+{{ optional_array_struct(type) -}}
+{%- endfor %}
+{%- for type in types %}
+struct {{ type | ppapi_type }} {
+ {%- for member in type.properties.itervalues() %}
+ {{ member | format_param_type }} {{ member.unix_name}};
+ {%- endfor %}
+};
+{{ optional_struct(type) -}}
+{{ array_struct(type) -}}
+{{ optional_array_struct(type) -}}
+{% endfor %}
+{%- for event in events.itervalues() %}
+typedef void {{ event | ppapi_type }}(
+ [in] uint32_t listener_id,
+ [inout] mem_t user_data{% if event.params %},{% endif %}
+ {%- for param in event.params %}
+ [in] {{ param | format_param_type }} {{ param.unix_name }}
+ {%- if not loop.last %},{% endif %}
+ {%- endfor -%}
+);
+{% endfor %}
+interface PPB_{{ name | classname }} {
+{% for function in functions.itervalues() %}
+ {{ function | return_type }} {{ function.name | classname }}(
+ [in] PP_Instance instance
+ {%- if function.params or function.callback or function.returns %},
+ {%- endif %}
+ {%- for param in function.params %}
+ [in] {{ param | format_param_type }} {{ param.unix_name }}
+ {%- if not loop.last or function.callback or function.returns %},
+ {%- endif %}
+ {%- endfor -%}
+ {%- if function.returns %}
+ [out] {{ function.returns | ppapi_type }} result,
+ {%- endif %}
+ {%- for param in function.callback.params %}
+ [out] {{ param | format_param_type }} {{ param.unix_name }},
+ {%- endfor %}
+ {%- if function.callback or function.returns %}
+ {%- if function | has_array_outs %}
+ [in] PP_ArrayOutput array_allocator,
+ {%- endif %}
+ [in] PP_CompletionCallback callback
+ {%- endif -%}
+ );
+{% endfor -%}
+{% for event in events.itervalues() %}
+ uint32_t Add{{ event.name | classname }}Listener (
+ [in] PP_Instance instance,
+ [in] {{ event | ppapi_type }} callback,
+ [inout] mem_t user_data);
+{% endfor %}
+};
+{% endblock %}