Enable python formatting in //tools/json_schema_compiler/
Python formatting using `git cl format` has been enabled in the codebase for a while now by including a .style.yapf file in your folder hierarchy. However back in 2020 an exception was added to ignore the json schema compiler folder, as the code in there uses a lot of chained function calls that are split across lines for readability and the formatter was condensing them into a much less readable state. This CL resolves this by using the line continuation character `\` for these chained function lines, allowing us to retain the more readable indentation and also enable the formatter by default. It also runs a full format over all the files in the directory, to get them into a consistent state where we can have the formatter enabled by default going forward with low impact. No behavior change is expected. Bug: 40711753 Change-Id: I6e10dc5af022ce0e3557099a84773aa9cc92d2e4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5804254 Commit-Queue: Tim <tjudkins@chromium.org> Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org> Cr-Commit-Position: refs/heads/main@{#1345613}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
91e2dd0507
commit
1e3d052650
.yapfignore
tools/json_schema_compiler
cc_generator.pycode_util.pycode_util_test.pycompiler.pycpp_bundle_generator.pycpp_bundle_generator_test.pycpp_generator.pycpp_namespace_environment.pycpp_type_generator.pycpp_type_generator_test.pycpp_util.pycpp_util_test.pyfeature_compiler.pyfeature_compiler_test.pyfeatures_cc_generator.pyfeatures_compiler.pyfeatures_h_generator.pygenerate_all_externs.pyh_generator.pyidl_schema.pyidl_schema_test.pyjs_externs_generator.pyjs_externs_generator_test.pyjs_interface_generator.pyjs_interface_generator_test.pyjs_util.pyjson_parse.pyjson_schema.pyjson_schema_test.pymemoize.pymodel.pymodel_test.pynamespace_resolver.pypreview.pyschema_loader.pyschema_util.pyschema_util_test.pyts_definition_generator.pyts_definition_generator_test.pyutil_cc_helper.pyweb_idl_schema.pyweb_idl_schema_test.py
@@ -5,6 +5,3 @@
|
|||||||
third_party/blink/tools/blinkpy/third_party/*
|
third_party/blink/tools/blinkpy/third_party/*
|
||||||
third_party/blink/web_tests/external/wpt/*
|
third_party/blink/web_tests/external/wpt/*
|
||||||
tools/valgrind/asan/third_party/asan_symbolize.py
|
tools/valgrind/asan/third_party/asan_symbolize.py
|
||||||
|
|
||||||
# TODO(crbug.com/1116155): Enable this for formatting by yapf.
|
|
||||||
tools/json_schema_compiler/*
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,19 +2,22 @@
|
|||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
class Code(object):
|
class Code(object):
|
||||||
"""A convenience object for constructing code.
|
"""A convenience object for constructing code.
|
||||||
|
|
||||||
Logically each object should be a block of code. All methods except |Render|
|
Logically each object should be a block of code. All methods except |Render|
|
||||||
and |IsEmpty| return self.
|
and |IsEmpty| return self.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, indent_size=2, comment_length=80):
|
def __init__(self, indent_size=2, comment_length=80):
|
||||||
self._code = []
|
self._code = []
|
||||||
self._indent_size = indent_size
|
self._indent_size = indent_size
|
||||||
self._comment_length = comment_length
|
self._comment_length = comment_length
|
||||||
self._line_prefixes = []
|
self._line_prefixes = []
|
||||||
|
|
||||||
def Append(self, line='',
|
def Append(self,
|
||||||
|
line='',
|
||||||
substitute=True,
|
substitute=True,
|
||||||
indent_level=None,
|
indent_level=None,
|
||||||
new_line=True,
|
new_line=True,
|
||||||
@@ -110,8 +113,11 @@ class Code(object):
|
|||||||
self.Append(line)
|
self.Append(line)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def Comment(self, comment, comment_prefix='// ',
|
def Comment(self,
|
||||||
wrap_indent=0, new_line=True):
|
comment,
|
||||||
|
comment_prefix='// ',
|
||||||
|
wrap_indent=0,
|
||||||
|
new_line=True):
|
||||||
"""Adds the given string as a comment.
|
"""Adds the given string as a comment.
|
||||||
|
|
||||||
Will split the comment if it's too long. Use mainly for variable length
|
Will split the comment if it's too long. Use mainly for variable length
|
||||||
@@ -119,6 +125,7 @@ class Code(object):
|
|||||||
|
|
||||||
Unaffected by code.Substitute().
|
Unaffected by code.Substitute().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Helper function to trim a comment to the maximum length, and return one
|
# Helper function to trim a comment to the maximum length, and return one
|
||||||
# line and the remainder of the comment.
|
# line and the remainder of the comment.
|
||||||
def trim_comment(comment, max_len):
|
def trim_comment(comment, max_len):
|
||||||
@@ -196,6 +203,7 @@ class Code(object):
|
|||||||
class Line(object):
|
class Line(object):
|
||||||
"""A line of code.
|
"""A line of code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, value, substitute=True):
|
def __init__(self, value, substitute=True):
|
||||||
self.value = value
|
self.value = value
|
||||||
self.substitute = substitute
|
self.substitute = substitute
|
||||||
|
@@ -6,7 +6,9 @@
|
|||||||
from code_util import Code
|
from code_util import Code
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class CodeTest(unittest.TestCase):
|
class CodeTest(unittest.TestCase):
|
||||||
|
|
||||||
def testAppend(self):
|
def testAppend(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Append('line')
|
c.Append('line')
|
||||||
@@ -14,61 +16,56 @@ class CodeTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testBlock(self):
|
def testBlock(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append('line')
|
(c.Append('line') \
|
||||||
.Sblock('sblock')
|
.Sblock('sblock') \
|
||||||
.Append('inner')
|
.Append('inner') \
|
||||||
.Append('moreinner')
|
.Append('moreinner') \
|
||||||
.Sblock('moresblock')
|
.Sblock('moresblock') \
|
||||||
.Append('inner')
|
.Append('inner') \
|
||||||
.Eblock('out')
|
.Eblock('out') \
|
||||||
.Append('inner')
|
.Append('inner') \
|
||||||
.Eblock('out')
|
.Eblock('out')
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'line\n'
|
'line\n'
|
||||||
'sblock\n'
|
'sblock\n'
|
||||||
' inner\n'
|
' inner\n'
|
||||||
' moreinner\n'
|
' moreinner\n'
|
||||||
' moresblock\n'
|
' moresblock\n'
|
||||||
' inner\n'
|
' inner\n'
|
||||||
' out\n'
|
' out\n'
|
||||||
' inner\n'
|
' inner\n'
|
||||||
'out',
|
'out', c.Render())
|
||||||
c.Render())
|
|
||||||
|
|
||||||
def testConcat(self):
|
def testConcat(self):
|
||||||
b = Code()
|
b = Code()
|
||||||
(b.Sblock('2')
|
(b.Sblock('2') \
|
||||||
.Append('2')
|
.Append('2') \
|
||||||
.Eblock('2')
|
.Eblock('2')
|
||||||
)
|
)
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Sblock('1')
|
(c.Sblock('1') \
|
||||||
.Concat(b)
|
.Concat(b) \
|
||||||
.Append('1')
|
.Append('1') \
|
||||||
.Eblock('1')
|
.Eblock('1')
|
||||||
)
|
)
|
||||||
self.assertMultiLineEqual(
|
self.assertMultiLineEqual('1\n'
|
||||||
'1\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 1\n'
|
||||||
' 1\n'
|
'1', c.Render())
|
||||||
'1',
|
|
||||||
c.Render())
|
|
||||||
d = Code()
|
d = Code()
|
||||||
a = Code()
|
a = Code()
|
||||||
a.Concat(d)
|
a.Concat(d)
|
||||||
self.assertEqual('', a.Render())
|
self.assertEqual('', a.Render())
|
||||||
a.Concat(c)
|
a.Concat(c)
|
||||||
self.assertEqual(
|
self.assertEqual('1\n'
|
||||||
'1\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 2\n'
|
||||||
' 2\n'
|
' 1\n'
|
||||||
' 1\n'
|
'1', a.Render())
|
||||||
'1',
|
|
||||||
a.Render())
|
|
||||||
|
|
||||||
def testConcatErrors(self):
|
def testConcatErrors(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
@@ -89,11 +86,9 @@ class CodeTest(unittest.TestCase):
|
|||||||
c.Append('%(var1)s %(var2)s %(var3)s')
|
c.Append('%(var1)s %(var2)s %(var3)s')
|
||||||
c.Append('%(var2)s %(var1)s %(var3)s')
|
c.Append('%(var2)s %(var1)s %(var3)s')
|
||||||
c.Substitute({'var1': 'one', 'var2': 'two', 'var3': 'three'})
|
c.Substitute({'var1': 'one', 'var2': 'two', 'var3': 'three'})
|
||||||
self.assertEqual(
|
self.assertEqual('one two one\n'
|
||||||
'one two one\n'
|
'one two three\n'
|
||||||
'one two three\n'
|
'two one three', c.Render())
|
||||||
'two one three',
|
|
||||||
c.Render())
|
|
||||||
|
|
||||||
def testSubstituteErrors(self):
|
def testSubstituteErrors(self):
|
||||||
# No unnamed placeholders allowed when substitute is run
|
# No unnamed placeholders allowed when substitute is run
|
||||||
@@ -118,14 +113,13 @@ class CodeTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testComment(self):
|
def testComment(self):
|
||||||
long_comment = ('This comment is ninety one characters in longness, '
|
long_comment = ('This comment is ninety one characters in longness, '
|
||||||
'that is, using a different word, length.')
|
'that is, using a different word, length.')
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Comment(long_comment)
|
c.Comment(long_comment)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'// This comment is ninety one characters '
|
'// This comment is ninety one characters '
|
||||||
'in longness, that is, using a different\n'
|
'in longness, that is, using a different\n'
|
||||||
'// word, length.',
|
'// word, length.', c.Render())
|
||||||
c.Render())
|
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Sblock('sblock')
|
c.Sblock('sblock')
|
||||||
c.Comment(long_comment)
|
c.Comment(long_comment)
|
||||||
@@ -139,27 +133,26 @@ class CodeTest(unittest.TestCase):
|
|||||||
'eblock\n'
|
'eblock\n'
|
||||||
'// This comment is ninety one characters in '
|
'// This comment is ninety one characters in '
|
||||||
'longness, that is, using a different\n'
|
'longness, that is, using a different\n'
|
||||||
'// word, length.',
|
'// word, length.', c.Render())
|
||||||
c.Render())
|
|
||||||
# Words that cannot be broken up are left as too long.
|
# Words that cannot be broken up are left as too long.
|
||||||
long_word = 'x' * 100
|
long_word = 'x' * 100
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Comment('xxx')
|
c.Comment('xxx')
|
||||||
c.Comment(long_word)
|
c.Comment(long_word)
|
||||||
c.Comment('xxx')
|
c.Comment('xxx')
|
||||||
self.assertEqual(
|
self.assertEqual('// xxx\n'
|
||||||
'// xxx\n'
|
'// ' + 'x' * 100 + '\n'
|
||||||
'// ' + 'x' * 100 + '\n'
|
'// xxx', c.Render())
|
||||||
'// xxx',
|
|
||||||
c.Render())
|
|
||||||
c = Code(indent_size=2, comment_length=40)
|
c = Code(indent_size=2, comment_length=40)
|
||||||
c.Comment('Pretend this is a Closure Compiler style comment, which should '
|
c.Comment(
|
||||||
'both wrap and indent', comment_prefix=' * ', wrap_indent=4)
|
'Pretend this is a Closure Compiler style comment, which should '
|
||||||
|
'both wrap and indent',
|
||||||
|
comment_prefix=' * ',
|
||||||
|
wrap_indent=4)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
' * Pretend this is a Closure Compiler\n'
|
' * Pretend this is a Closure Compiler\n'
|
||||||
' * style comment, which should both\n'
|
' * style comment, which should both\n'
|
||||||
' * wrap and indent',
|
' * wrap and indent', c.Render())
|
||||||
c.Render())
|
|
||||||
|
|
||||||
def testCommentWithSpecialCharacters(self):
|
def testCommentWithSpecialCharacters(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
@@ -170,8 +163,7 @@ class CodeTest(unittest.TestCase):
|
|||||||
d.Append('90')
|
d.Append('90')
|
||||||
d.Concat(c)
|
d.Concat(c)
|
||||||
self.assertEqual('90\n'
|
self.assertEqual('90\n'
|
||||||
'// 20% of 80%s',
|
'// 20% of 80%s', d.Render())
|
||||||
d.Render())
|
|
||||||
|
|
||||||
def testLinePrefixes(self):
|
def testLinePrefixes(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
@@ -192,8 +184,7 @@ class CodeTest(unittest.TestCase):
|
|||||||
' * x: y\n'
|
' * x: y\n'
|
||||||
' * }\n'
|
' * }\n'
|
||||||
' * }}\n'
|
' * }}\n'
|
||||||
' */',
|
' */', output)
|
||||||
output)
|
|
||||||
|
|
||||||
def testSameLineAppendConcatComment(self):
|
def testSameLineAppendConcatComment(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
@@ -206,12 +197,13 @@ class CodeTest(unittest.TestCase):
|
|||||||
c = Code()
|
c = Code()
|
||||||
c.Append('This is a')
|
c.Append('This is a')
|
||||||
c.Comment(' spectacular 80-character line thingy ' +
|
c.Comment(' spectacular 80-character line thingy ' +
|
||||||
'that fits wonderfully everywhere.',
|
'that fits wonderfully everywhere.',
|
||||||
comment_prefix='',
|
comment_prefix='',
|
||||||
new_line=False)
|
new_line=False)
|
||||||
self.assertEqual('This is a spectacular 80-character line thingy that ' +
|
self.assertEqual(
|
||||||
'fits wonderfully everywhere.',
|
'This is a spectacular 80-character line thingy that ' +
|
||||||
c.Render())
|
'fits wonderfully everywhere.', c.Render())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@@ -101,12 +101,10 @@ def GenerateSchema(
|
|||||||
|
|
||||||
# Construct the type generator with all the namespaces in this model.
|
# Construct the type generator with all the namespaces in this model.
|
||||||
schema_dir = os.path.dirname(os.path.relpath(file_paths[0], root))
|
schema_dir = os.path.dirname(os.path.relpath(file_paths[0], root))
|
||||||
namespace_resolver = NamespaceResolver(
|
namespace_resolver = NamespaceResolver(root, schema_dir, include_rules,
|
||||||
root, schema_dir, include_rules, cpp_namespace_pattern
|
cpp_namespace_pattern)
|
||||||
)
|
type_generator = CppTypeGenerator(api_model, namespace_resolver,
|
||||||
type_generator = CppTypeGenerator(
|
default_namespace)
|
||||||
api_model, namespace_resolver, default_namespace
|
|
||||||
)
|
|
||||||
if generator_name in ('cpp-bundle-registration', 'cpp-bundle-schema'):
|
if generator_name in ('cpp-bundle-registration', 'cpp-bundle-schema'):
|
||||||
cpp_bundle_generator = CppBundleGenerator(
|
cpp_bundle_generator = CppBundleGenerator(
|
||||||
root,
|
root,
|
||||||
@@ -187,12 +185,11 @@ if __name__ == '__main__':
|
|||||||
default='.',
|
default='.',
|
||||||
help=(
|
help=(
|
||||||
'logical include root directory. Path to schema files from specified'
|
'logical include root directory. Path to schema files from specified'
|
||||||
' dir will be the include path.'
|
' dir will be the include path.'),
|
||||||
),
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
'-d', '--destdir', help='root directory to output generated files.'
|
|
||||||
)
|
)
|
||||||
|
parser.add_option('-d',
|
||||||
|
'--destdir',
|
||||||
|
help='root directory to output generated files.')
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-n',
|
'-n',
|
||||||
'--namespace',
|
'--namespace',
|
||||||
@@ -203,11 +200,9 @@ if __name__ == '__main__':
|
|||||||
'-b',
|
'-b',
|
||||||
'--bundle-name',
|
'--bundle-name',
|
||||||
default='',
|
default='',
|
||||||
help=(
|
help=('A string to prepend to generated bundle class names, so that '
|
||||||
'A string to prepend to generated bundle class names, so that '
|
'multiple bundle rules can be used without conflicting. '
|
||||||
'multiple bundle rules can be used without conflicting. '
|
'Only used with one of the cpp-bundle generators.'),
|
||||||
'Only used with one of the cpp-bundle generators.'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-g',
|
'-g',
|
||||||
@@ -216,9 +211,7 @@ if __name__ == '__main__':
|
|||||||
choices=GENERATORS,
|
choices=GENERATORS,
|
||||||
help=(
|
help=(
|
||||||
'The generator to use to build the output code. Supported values are'
|
'The generator to use to build the output code. Supported values are'
|
||||||
' %s'
|
' %s') % GENERATORS,
|
||||||
)
|
|
||||||
% GENERATORS,
|
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-i',
|
'-i',
|
||||||
@@ -229,11 +222,9 @@ if __name__ == '__main__':
|
|||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-I',
|
'-I',
|
||||||
'--include-rules',
|
'--include-rules',
|
||||||
help=(
|
help=('A list of paths to include when searching for referenced objects,'
|
||||||
'A list of paths to include when searching for referenced objects,'
|
" with the namespace separated by a ':'. Example: "
|
||||||
" with the namespace separated by a ':'. Example: "
|
'/foo/bar:Foo::Bar::%(namespace)s'),
|
||||||
'/foo/bar:Foo::Bar::%(namespace)s'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
(opts, file_paths) = parser.parse_args()
|
(opts, file_paths) = parser.parse_args()
|
||||||
@@ -242,28 +233,23 @@ if __name__ == '__main__':
|
|||||||
sys.exit(0) # This is OK as a no-op
|
sys.exit(0) # This is OK as a no-op
|
||||||
|
|
||||||
# Unless in bundle mode, only one file should be specified.
|
# Unless in bundle mode, only one file should be specified.
|
||||||
if (
|
if (opts.generator not in ('cpp-bundle-registration', 'cpp-bundle-schema')
|
||||||
opts.generator not in ('cpp-bundle-registration', 'cpp-bundle-schema')
|
and len(file_paths) > 1):
|
||||||
and len(file_paths) > 1
|
|
||||||
):
|
|
||||||
# TODO(sashab): Could also just use file_paths[0] here and not complain.
|
# TODO(sashab): Could also just use file_paths[0] here and not complain.
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Unless in bundle mode, only one file can be specified at a time.'
|
'Unless in bundle mode, only one file can be specified at a time.')
|
||||||
)
|
|
||||||
|
|
||||||
def split_path_and_namespace(path_and_namespace):
|
def split_path_and_namespace(path_and_namespace):
|
||||||
if ':' not in path_and_namespace:
|
if ':' not in path_and_namespace:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Invalid include rule "%s". Rules must be of the form path:namespace'
|
'Invalid include rule "%s". Rules must be of the form path:namespace'
|
||||||
% path_and_namespace
|
% path_and_namespace)
|
||||||
)
|
|
||||||
return path_and_namespace.split(':', 1)
|
return path_and_namespace.split(':', 1)
|
||||||
|
|
||||||
include_rules = []
|
include_rules = []
|
||||||
if opts.include_rules:
|
if opts.include_rules:
|
||||||
include_rules = list(
|
include_rules = list(
|
||||||
map(split_path_and_namespace, shlex.split(opts.include_rules))
|
map(split_path_and_namespace, shlex.split(opts.include_rules)))
|
||||||
)
|
|
||||||
|
|
||||||
result = GenerateSchema(
|
result = GenerateSchema(
|
||||||
opts.generator,
|
opts.generator,
|
||||||
|
@@ -26,6 +26,7 @@ def _RemoveKey(node, key, type_restriction):
|
|||||||
for value in node:
|
for value in node:
|
||||||
_RemoveKey(value, key, type_restriction)
|
_RemoveKey(value, key, type_restriction)
|
||||||
|
|
||||||
|
|
||||||
def _RemoveUnneededFields(schema):
|
def _RemoveUnneededFields(schema):
|
||||||
"""Returns a copy of |schema| with fields that aren't necessary at runtime
|
"""Returns a copy of |schema| with fields that aren't necessary at runtime
|
||||||
removed.
|
removed.
|
||||||
@@ -41,6 +42,7 @@ def _RemoveUnneededFields(schema):
|
|||||||
_RemoveKey(ret, 'manifest_keys', object)
|
_RemoveKey(ret, 'manifest_keys', object)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _PrefixSchemaWithNamespace(schema):
|
def _PrefixSchemaWithNamespace(schema):
|
||||||
"""Modifies |schema| in place to prefix all types and references with a
|
"""Modifies |schema| in place to prefix all types and references with a
|
||||||
namespace, if they aren't already qualified. That is, in the tabs API, this
|
namespace, if they aren't already qualified. That is, in the tabs API, this
|
||||||
@@ -49,10 +51,11 @@ def _PrefixSchemaWithNamespace(schema):
|
|||||||
"""
|
"""
|
||||||
assert isinstance(schema, dict), "Schema is unexpected type"
|
assert isinstance(schema, dict), "Schema is unexpected type"
|
||||||
namespace = schema['namespace']
|
namespace = schema['namespace']
|
||||||
|
|
||||||
def prefix(obj, key, mandatory):
|
def prefix(obj, key, mandatory):
|
||||||
if not key in obj:
|
if not key in obj:
|
||||||
assert not mandatory, (
|
assert not mandatory, ('Required key "%s" is not present in object.' %
|
||||||
'Required key "%s" is not present in object.' % key)
|
key)
|
||||||
return
|
return
|
||||||
assert type(obj[key]) is str
|
assert type(obj[key]) is str
|
||||||
if obj[key].find('.') == -1:
|
if obj[key].find('.') == -1:
|
||||||
@@ -73,6 +76,7 @@ def _PrefixSchemaWithNamespace(schema):
|
|||||||
prefix(val, '$ref', False)
|
prefix(val, '$ref', False)
|
||||||
for key, sub_val in val.items():
|
for key, sub_val in val.items():
|
||||||
prefix_refs(sub_val)
|
prefix_refs(sub_val)
|
||||||
|
|
||||||
prefix_refs(schema)
|
prefix_refs(schema)
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
@@ -81,15 +85,8 @@ class CppBundleGenerator(object):
|
|||||||
"""This class contains methods to generate code based on multiple schemas.
|
"""This class contains methods to generate code based on multiple schemas.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self, root, model, api_defs, cpp_type_generator,
|
||||||
root,
|
cpp_namespace_pattern, bundle_name, source_file_dir, impl_dir):
|
||||||
model,
|
|
||||||
api_defs,
|
|
||||||
cpp_type_generator,
|
|
||||||
cpp_namespace_pattern,
|
|
||||||
bundle_name,
|
|
||||||
source_file_dir,
|
|
||||||
impl_dir):
|
|
||||||
self._root = root
|
self._root = root
|
||||||
self._model = model
|
self._model = model
|
||||||
self._api_defs = api_defs
|
self._api_defs = api_defs
|
||||||
@@ -196,9 +193,9 @@ class CppBundleGenerator(object):
|
|||||||
if function.nocompile:
|
if function.nocompile:
|
||||||
continue
|
continue
|
||||||
namespace_types_name = JsFunctionNameToClassName(
|
namespace_types_name = JsFunctionNameToClassName(
|
||||||
namespace.name, type_.name)
|
namespace.name, type_.name)
|
||||||
c.Concat(self._GenerateRegistrationEntry(namespace_types_name,
|
c.Concat(
|
||||||
function))
|
self._GenerateRegistrationEntry(namespace_types_name, function))
|
||||||
|
|
||||||
if namespace_ifdefs is not None:
|
if namespace_ifdefs is not None:
|
||||||
c.Append("#endif // %s" % namespace_ifdefs, indent_level=0)
|
c.Append("#endif // %s" % namespace_ifdefs, indent_level=0)
|
||||||
@@ -218,6 +215,7 @@ class CppBundleGenerator(object):
|
|||||||
|
|
||||||
class _APIHGenerator(object):
|
class _APIHGenerator(object):
|
||||||
"""Generates the header for API registration / declaration"""
|
"""Generates the header for API registration / declaration"""
|
||||||
|
|
||||||
def __init__(self, cpp_bundle):
|
def __init__(self, cpp_bundle):
|
||||||
self._bundle = cpp_bundle
|
self._bundle = cpp_bundle
|
||||||
|
|
||||||
@@ -234,7 +232,7 @@ class _APIHGenerator(object):
|
|||||||
self._bundle._GenerateBundleClass('GeneratedFunctionRegistry'))
|
self._bundle._GenerateBundleClass('GeneratedFunctionRegistry'))
|
||||||
c.Sblock(' public:')
|
c.Sblock(' public:')
|
||||||
c.Append('static void RegisterAll('
|
c.Append('static void RegisterAll('
|
||||||
'ExtensionFunctionRegistry* registry);')
|
'ExtensionFunctionRegistry* registry);')
|
||||||
c.Eblock('};')
|
c.Eblock('};')
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
|
c.Concat(cpp_util.CloseNamespace(self._bundle._cpp_namespace))
|
||||||
@@ -251,9 +249,8 @@ class _APICCGenerator(object):
|
|||||||
c = code_util.Code()
|
c = code_util.Code()
|
||||||
c.Append(cpp_util.CHROMIUM_LICENSE)
|
c.Append(cpp_util.CHROMIUM_LICENSE)
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Append('#include "%s"' % (
|
c.Append('#include "%s"' % (cpp_util.ToPosixPath(
|
||||||
cpp_util.ToPosixPath(os.path.join(self._bundle._impl_dir,
|
os.path.join(self._bundle._impl_dir, 'generated_api_registration.h'))))
|
||||||
'generated_api_registration.h'))))
|
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Append('#include "build/build_config.h"')
|
c.Append('#include "build/build_config.h"')
|
||||||
c.Append('#include "build/chromeos_buildflags.h"')
|
c.Append('#include "build/chromeos_buildflags.h"')
|
||||||
@@ -261,17 +258,15 @@ class _APICCGenerator(object):
|
|||||||
for namespace in self._bundle._model.namespaces.values():
|
for namespace in self._bundle._model.namespaces.values():
|
||||||
namespace_name = namespace.unix_name.replace("experimental_", "")
|
namespace_name = namespace.unix_name.replace("experimental_", "")
|
||||||
implementation_header = namespace.compiler_options.get(
|
implementation_header = namespace.compiler_options.get(
|
||||||
"implemented_in",
|
"implemented_in", "%s/%s/%s_api.h" %
|
||||||
"%s/%s/%s_api.h" % (self._bundle._impl_dir,
|
(self._bundle._impl_dir, namespace_name, namespace_name))
|
||||||
namespace_name,
|
|
||||||
namespace_name))
|
|
||||||
if not os.path.exists(
|
if not os.path.exists(
|
||||||
os.path.join(self._bundle._root,
|
os.path.join(self._bundle._root,
|
||||||
os.path.normpath(implementation_header))):
|
os.path.normpath(implementation_header))):
|
||||||
if "implemented_in" in namespace.compiler_options:
|
if "implemented_in" in namespace.compiler_options:
|
||||||
raise ValueError('Header file for namespace "%s" specified in '
|
raise ValueError('Header file for namespace "%s" specified in '
|
||||||
'compiler_options not found: %s' %
|
'compiler_options not found: %s' %
|
||||||
(namespace.unix_name, implementation_header))
|
(namespace.unix_name, implementation_header))
|
||||||
continue
|
continue
|
||||||
ifdefs = self._bundle._GetPlatformIfdefs(namespace)
|
ifdefs = self._bundle._GetPlatformIfdefs(namespace)
|
||||||
if ifdefs is not None:
|
if ifdefs is not None:
|
||||||
@@ -283,7 +278,7 @@ class _APICCGenerator(object):
|
|||||||
c.Append("#endif // %s" % ifdefs, indent_level=0)
|
c.Append("#endif // %s" % ifdefs, indent_level=0)
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Append('#include '
|
c.Append('#include '
|
||||||
'"extensions/browser/extension_function_registry.h"')
|
'"extensions/browser/extension_function_registry.h"')
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
|
c.Concat(cpp_util.OpenNamespace(self._bundle._cpp_namespace))
|
||||||
c.Append()
|
c.Append()
|
||||||
@@ -296,6 +291,7 @@ class _APICCGenerator(object):
|
|||||||
|
|
||||||
class _SchemasHGenerator(object):
|
class _SchemasHGenerator(object):
|
||||||
"""Generates a code_util.Code object for the generated schemas .h file"""
|
"""Generates a code_util.Code object for the generated schemas .h file"""
|
||||||
|
|
||||||
def __init__(self, cpp_bundle):
|
def __init__(self, cpp_bundle):
|
||||||
self._bundle = cpp_bundle
|
self._bundle = cpp_bundle
|
||||||
|
|
||||||
@@ -322,8 +318,7 @@ class _SchemasHGenerator(object):
|
|||||||
def _FormatNameAsConstant(name):
|
def _FormatNameAsConstant(name):
|
||||||
"""Formats a name to be a C++ constant of the form kConstantName"""
|
"""Formats a name to be a C++ constant of the form kConstantName"""
|
||||||
name = '%s%s' % (name[0].upper(), name[1:])
|
name = '%s%s' % (name[0].upper(), name[1:])
|
||||||
return 'k%s' % re.sub('_[a-z]',
|
return 'k%s' % re.sub('_[a-z]', lambda m: m.group(0)[1].upper(),
|
||||||
lambda m: m.group(0)[1].upper(),
|
|
||||||
name.replace('.', '_'))
|
name.replace('.', '_'))
|
||||||
|
|
||||||
|
|
||||||
@@ -337,9 +332,8 @@ class _SchemasCCGenerator(object):
|
|||||||
c = code_util.Code()
|
c = code_util.Code()
|
||||||
c.Append(cpp_util.CHROMIUM_LICENSE)
|
c.Append(cpp_util.CHROMIUM_LICENSE)
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Append('#include "%s"' % (
|
c.Append('#include "%s"' % (cpp_util.ToPosixPath(
|
||||||
cpp_util.ToPosixPath(os.path.join(self._bundle._source_file_dir,
|
os.path.join(self._bundle._source_file_dir, 'generated_schemas.h'))))
|
||||||
'generated_schemas.h'))))
|
|
||||||
c.Append()
|
c.Append()
|
||||||
c.Append('#include <algorithm>')
|
c.Append('#include <algorithm>')
|
||||||
c.Append('#include <iterator>')
|
c.Append('#include <iterator>')
|
||||||
@@ -351,7 +345,7 @@ class _SchemasCCGenerator(object):
|
|||||||
for api in self._bundle._api_defs:
|
for api in self._bundle._api_defs:
|
||||||
namespace = self._bundle._model.namespaces[api.get('namespace')]
|
namespace = self._bundle._model.namespaces[api.get('namespace')]
|
||||||
json_content = json.dumps(_PrefixSchemaWithNamespace(
|
json_content = json.dumps(_PrefixSchemaWithNamespace(
|
||||||
_RemoveUnneededFields(api)),
|
_RemoveUnneededFields(api)),
|
||||||
separators=(',', ':'))
|
separators=(',', ':'))
|
||||||
# This will output a valid JSON C string. Note that some schemas are
|
# This will output a valid JSON C string. Note that some schemas are
|
||||||
# too large to compile on windows. Split the JSON up into several
|
# too large to compile on windows. Split the JSON up into several
|
||||||
@@ -381,8 +375,10 @@ class _SchemasCCGenerator(object):
|
|||||||
c.Append('static constexpr auto kSchemas = '
|
c.Append('static constexpr auto kSchemas = '
|
||||||
'base::MakeFixedFlatMap<std::string_view, std::string_view>({')
|
'base::MakeFixedFlatMap<std::string_view, std::string_view>({')
|
||||||
c.Sblock()
|
c.Sblock()
|
||||||
namespaces = [self._bundle._model.namespaces[api.get('namespace')].name
|
namespaces = [
|
||||||
for api in self._bundle._api_defs]
|
self._bundle._model.namespaces[api.get('namespace')].name
|
||||||
|
for api in self._bundle._api_defs
|
||||||
|
]
|
||||||
for namespace in sorted(namespaces):
|
for namespace in sorted(namespaces):
|
||||||
schema_constant_name = _FormatNameAsConstant(namespace)
|
schema_constant_name = _FormatNameAsConstant(namespace)
|
||||||
c.Append('{"%s", %s},' % (namespace, schema_constant_name))
|
c.Append('{"%s", %s},' % (namespace, schema_constant_name))
|
||||||
|
@@ -10,26 +10,29 @@ import json_schema
|
|||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
def _createCppBundleGenerator(file_path):
|
def _createCppBundleGenerator(file_path):
|
||||||
json_object = json_schema.Load(file_path)
|
json_object = json_schema.Load(file_path)
|
||||||
model = Model()
|
model = Model()
|
||||||
model.AddNamespace(json_object[0], file_path)
|
model.AddNamespace(json_object[0], file_path)
|
||||||
cpp_bundle_generator = CppBundleGenerator(
|
cpp_bundle_generator = CppBundleGenerator(None, model, None, None,
|
||||||
None, model, None, None, 'generated_api_schemas',
|
'generated_api_schemas', None, None,
|
||||||
None, None, None)
|
None)
|
||||||
return (cpp_bundle_generator, model)
|
return (cpp_bundle_generator, model)
|
||||||
|
|
||||||
|
|
||||||
def _getPlatformIfdefs(cpp_bundle_generator, model):
|
def _getPlatformIfdefs(cpp_bundle_generator, model):
|
||||||
return cpp_bundle_generator._GetPlatformIfdefs(
|
return cpp_bundle_generator._GetPlatformIfdefs(
|
||||||
list(list(model.namespaces.values())[0].functions.values())[0])
|
list(list(model.namespaces.values())[0].functions.values())[0])
|
||||||
|
|
||||||
|
|
||||||
class CppBundleGeneratorTest(unittest.TestCase):
|
class CppBundleGeneratorTest(unittest.TestCase):
|
||||||
|
|
||||||
def testIfDefsForWinLinux(self):
|
def testIfDefsForWinLinux(self):
|
||||||
cpp_bundle_generator, model = _createCppBundleGenerator(
|
cpp_bundle_generator, model = _createCppBundleGenerator(
|
||||||
'test/function_platform_win_linux.json')
|
'test/function_platform_win_linux.json')
|
||||||
self.assertEqual(
|
self.assertEqual('BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)',
|
||||||
'BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)',
|
_getPlatformIfdefs(cpp_bundle_generator, model))
|
||||||
_getPlatformIfdefs(cpp_bundle_generator, model))
|
|
||||||
|
|
||||||
def testIfDefsForAll(self):
|
def testIfDefsForAll(self):
|
||||||
cpp_bundle_generator, model = _createCppBundleGenerator(
|
cpp_bundle_generator, model = _createCppBundleGenerator(
|
||||||
@@ -43,7 +46,7 @@ class CppBundleGeneratorTest(unittest.TestCase):
|
|||||||
cpp_bundle_generator, model = _createCppBundleGenerator(
|
cpp_bundle_generator, model = _createCppBundleGenerator(
|
||||||
'test/function_platform_chromeos.json')
|
'test/function_platform_chromeos.json')
|
||||||
self.assertEqual('BUILDFLAG(IS_CHROMEOS_ASH)',
|
self.assertEqual('BUILDFLAG(IS_CHROMEOS_ASH)',
|
||||||
_getPlatformIfdefs(cpp_bundle_generator, model))
|
_getPlatformIfdefs(cpp_bundle_generator, model))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -5,7 +5,9 @@
|
|||||||
from cc_generator import CCGenerator
|
from cc_generator import CCGenerator
|
||||||
from h_generator import HGenerator
|
from h_generator import HGenerator
|
||||||
|
|
||||||
|
|
||||||
class CppGenerator(object):
|
class CppGenerator(object):
|
||||||
|
|
||||||
def __init__(self, type_generator):
|
def __init__(self, type_generator):
|
||||||
self.h_generator = HGenerator(type_generator)
|
self.h_generator = HGenerator(type_generator)
|
||||||
self.cc_generator = CCGenerator(type_generator)
|
self.cc_generator = CCGenerator(type_generator)
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
class CppNamespaceEnvironment(object):
|
class CppNamespaceEnvironment(object):
|
||||||
|
|
||||||
def __init__(self, namespace_pattern):
|
def __init__(self, namespace_pattern):
|
||||||
self.namespace_pattern = namespace_pattern
|
self.namespace_pattern = namespace_pattern
|
||||||
|
@@ -8,11 +8,13 @@ import cpp_util
|
|||||||
from json_parse import OrderedDict
|
from json_parse import OrderedDict
|
||||||
import schema_util
|
import schema_util
|
||||||
|
|
||||||
|
|
||||||
class _TypeDependency(object):
|
class _TypeDependency(object):
|
||||||
"""Contains information about a dependency a namespace has on a type: the
|
"""Contains information about a dependency a namespace has on a type: the
|
||||||
type's model, and whether that dependency is "hard" meaning that it cannot be
|
type's model, and whether that dependency is "hard" meaning that it cannot be
|
||||||
forward declared.
|
forward declared.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, type_, hard=False):
|
def __init__(self, type_, hard=False):
|
||||||
self.type_ = type_
|
self.type_ = type_
|
||||||
self.hard = hard
|
self.hard = hard
|
||||||
@@ -25,6 +27,7 @@ class CppTypeGenerator(object):
|
|||||||
"""Manages the types of properties and provides utilities for getting the
|
"""Manages the types of properties and provides utilities for getting the
|
||||||
C++ type out of a model.Property
|
C++ type out of a model.Property
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, model, namespace_resolver, default_namespace=None):
|
def __init__(self, model, namespace_resolver, default_namespace=None):
|
||||||
"""Creates a cpp_type_generator. The given root_namespace should be of the
|
"""Creates a cpp_type_generator. The given root_namespace should be of the
|
||||||
format extensions::api::sub. The generator will generate code suitable for
|
format extensions::api::sub. The generator will generate code suitable for
|
||||||
@@ -40,9 +43,8 @@ class CppTypeGenerator(object):
|
|||||||
typename in an optional, for the regular case, or uses a base::expected for
|
typename in an optional, for the regular case, or uses a base::expected for
|
||||||
when it should support string errors.
|
when it should support string errors.
|
||||||
"""
|
"""
|
||||||
return (('base::expected<{typename}, std::u16string>'
|
return (('base::expected<{typename}, std::u16string>' if support_errors else
|
||||||
if support_errors else 'std::optional<{typename}>')
|
'std::optional<{typename}>').format(typename=typename))
|
||||||
.format(typename=typename))
|
|
||||||
|
|
||||||
def GetEnumNoneValue(self, type_, full_name=True):
|
def GetEnumNoneValue(self, type_, full_name=True):
|
||||||
"""Gets the enum value in the given model. Property indicating no value has
|
"""Gets the enum value in the given model. Property indicating no value has
|
||||||
@@ -91,7 +93,7 @@ class CppTypeGenerator(object):
|
|||||||
result = ''
|
result = ''
|
||||||
for char in name:
|
for char in name:
|
||||||
if char in {'_', '-'}:
|
if char in {'_', '-'}:
|
||||||
change_to_upper=True
|
change_to_upper = True
|
||||||
elif change_to_upper:
|
elif change_to_upper:
|
||||||
# Numbers must be kept separate, for better readability (e.g. kX86_64).
|
# Numbers must be kept separate, for better readability (e.g. kX86_64).
|
||||||
if char.isnumeric() and result and result[-1].isnumeric():
|
if char.isnumeric() and result and result[-1].isnumeric():
|
||||||
@@ -125,9 +127,9 @@ class CppTypeGenerator(object):
|
|||||||
prefix = '{classname}::'.format(classname=classname)
|
prefix = '{classname}::'.format(classname=classname)
|
||||||
# We kCamelCase the string, also removing any _ from the name, to allow
|
# We kCamelCase the string, also removing any _ from the name, to allow
|
||||||
# SHOUTY_CASE keys to be kCamelCase as well.
|
# SHOUTY_CASE keys to be kCamelCase as well.
|
||||||
return '{prefix}k{name}'.format(
|
return '{prefix}k{name}'.format(prefix=prefix,
|
||||||
prefix=prefix,
|
name=self.FormatStringForEnumValue(
|
||||||
name=self.FormatStringForEnumValue(enum_value.name))
|
enum_value.name))
|
||||||
|
|
||||||
def GetCppType(self, type_, is_optional=False):
|
def GetCppType(self, type_, is_optional=False):
|
||||||
"""Translates a model.Property or model.Type into its C++ type.
|
"""Translates a model.Property or model.Type into its C++ type.
|
||||||
@@ -155,8 +157,7 @@ class CppTypeGenerator(object):
|
|||||||
cpp_type = 'double'
|
cpp_type = 'double'
|
||||||
elif type_.property_type == PropertyType.STRING:
|
elif type_.property_type == PropertyType.STRING:
|
||||||
cpp_type = 'std::string'
|
cpp_type = 'std::string'
|
||||||
elif type_.property_type in (PropertyType.ENUM,
|
elif type_.property_type in (PropertyType.ENUM, PropertyType.OBJECT,
|
||||||
PropertyType.OBJECT,
|
|
||||||
PropertyType.CHOICES):
|
PropertyType.CHOICES):
|
||||||
if self._default_namespace is type_.namespace:
|
if self._default_namespace is type_.namespace:
|
||||||
cpp_type = cpp_util.Classname(type_.name)
|
cpp_type = cpp_util.Classname(type_.name)
|
||||||
@@ -164,8 +165,7 @@ class CppTypeGenerator(object):
|
|||||||
cpp_namespace = cpp_util.GetCppNamespace(
|
cpp_namespace = cpp_util.GetCppNamespace(
|
||||||
type_.namespace.environment.namespace_pattern,
|
type_.namespace.environment.namespace_pattern,
|
||||||
type_.namespace.unix_name)
|
type_.namespace.unix_name)
|
||||||
cpp_type = '%s::%s' % (cpp_namespace,
|
cpp_type = '%s::%s' % (cpp_namespace, cpp_util.Classname(type_.name))
|
||||||
cpp_util.Classname(type_.name))
|
|
||||||
elif type_.property_type == PropertyType.ANY:
|
elif type_.property_type == PropertyType.ANY:
|
||||||
cpp_type = 'base::Value'
|
cpp_type = 'base::Value'
|
||||||
elif type_.property_type == PropertyType.FUNCTION:
|
elif type_.property_type == PropertyType.FUNCTION:
|
||||||
@@ -201,10 +201,9 @@ class CppTypeGenerator(object):
|
|||||||
return cpp_type
|
return cpp_type
|
||||||
|
|
||||||
def IsCopyable(self, type_):
|
def IsCopyable(self, type_):
|
||||||
return not (self.FollowRef(type_).property_type in (PropertyType.ANY,
|
return not (self.FollowRef(type_).property_type
|
||||||
PropertyType.ARRAY,
|
in (PropertyType.ANY, PropertyType.ARRAY, PropertyType.OBJECT,
|
||||||
PropertyType.OBJECT,
|
PropertyType.CHOICES))
|
||||||
PropertyType.CHOICES))
|
|
||||||
|
|
||||||
def GenerateForwardDeclarations(self):
|
def GenerateForwardDeclarations(self):
|
||||||
"""Returns the forward declarations for self._default_namespace.
|
"""Returns the forward declarations for self._default_namespace.
|
||||||
@@ -212,17 +211,16 @@ class CppTypeGenerator(object):
|
|||||||
c = Code()
|
c = Code()
|
||||||
for namespace, deps in self._NamespaceTypeDependencies().items():
|
for namespace, deps in self._NamespaceTypeDependencies().items():
|
||||||
filtered_deps = [
|
filtered_deps = [
|
||||||
dep for dep in deps
|
dep for dep in deps
|
||||||
# Add more ways to forward declare things as necessary.
|
# Add more ways to forward declare things as necessary.
|
||||||
if (not dep.hard and
|
if (not dep.hard and dep.type_.property_type in (PropertyType.CHOICES,
|
||||||
dep.type_.property_type in (PropertyType.CHOICES,
|
PropertyType.OBJECT))
|
||||||
PropertyType.OBJECT))]
|
]
|
||||||
if not filtered_deps:
|
if not filtered_deps:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cpp_namespace = cpp_util.GetCppNamespace(
|
cpp_namespace = cpp_util.GetCppNamespace(
|
||||||
namespace.environment.namespace_pattern,
|
namespace.environment.namespace_pattern, namespace.unix_name)
|
||||||
namespace.unix_name)
|
|
||||||
c.Concat(cpp_util.OpenNamespace(cpp_namespace))
|
c.Concat(cpp_util.OpenNamespace(cpp_namespace))
|
||||||
for dep in filtered_deps:
|
for dep in filtered_deps:
|
||||||
c.Append('struct %s;' % dep.type_.name)
|
c.Append('struct %s;' % dep.type_.name)
|
||||||
@@ -236,9 +234,9 @@ class CppTypeGenerator(object):
|
|||||||
|
|
||||||
# The inclusion of the std::string_view header is dependent on either the
|
# The inclusion of the std::string_view header is dependent on either the
|
||||||
# presence of enums, or manifest keys.
|
# presence of enums, or manifest keys.
|
||||||
include_string_view = (self._default_namespace.manifest_keys or
|
include_string_view = (self._default_namespace.manifest_keys or any(
|
||||||
any(type_.property_type is PropertyType.ENUM for type_ in
|
type_.property_type is PropertyType.ENUM
|
||||||
self._default_namespace.types.values()))
|
for type_ in self._default_namespace.types.values()))
|
||||||
|
|
||||||
if include_string_view:
|
if include_string_view:
|
||||||
c.Append('#include <string_view>')
|
c.Append('#include <string_view>')
|
||||||
@@ -246,11 +244,10 @@ class CppTypeGenerator(object):
|
|||||||
# The header for `base::expected` should be included whenever error messages
|
# The header for `base::expected` should be included whenever error messages
|
||||||
# are supposed to be returned, which only occurs with object, choices, or
|
# are supposed to be returned, which only occurs with object, choices, or
|
||||||
# functions.
|
# functions.
|
||||||
if (generate_error_messages and (
|
if (generate_error_messages
|
||||||
len(self._default_namespace.functions.values()) or
|
and (len(self._default_namespace.functions.values()) or any(
|
||||||
any(type_.property_type in
|
type_.property_type in [PropertyType.OBJECT, PropertyType.CHOICES]
|
||||||
[PropertyType.OBJECT, PropertyType.CHOICES] for type_ in
|
for type_ in self._default_namespace.types.values()))):
|
||||||
self._default_namespace.types.values()))):
|
|
||||||
c.Append('#include "base/types/expected.h"')
|
c.Append('#include "base/types/expected.h"')
|
||||||
|
|
||||||
# Note: It's possible that there are multiple dependencies from the same
|
# Note: It's possible that there are multiple dependencies from the same
|
||||||
@@ -361,11 +358,7 @@ class CppTypeGenerator(object):
|
|||||||
cpp_value = '"%s"' % cpp_value
|
cpp_value = '"%s"' % cpp_value
|
||||||
cpp_type = 'char'
|
cpp_type = 'char'
|
||||||
cpp_name = '%s[]' % cpp_name
|
cpp_name = '%s[]' % cpp_name
|
||||||
c.Append(line % {
|
c.Append(line % {"type": cpp_type, "name": cpp_name, "value": cpp_value})
|
||||||
"type": cpp_type,
|
|
||||||
"name": cpp_name,
|
|
||||||
"value": cpp_value
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
has_child_code = False
|
has_child_code = False
|
||||||
c.Sblock('namespace %s {' % prop.name)
|
c.Sblock('namespace %s {' % prop.name)
|
||||||
|
@@ -12,7 +12,9 @@ import unittest
|
|||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
class _FakeSchemaLoader(object):
|
class _FakeSchemaLoader(object):
|
||||||
|
|
||||||
def __init__(self, model):
|
def __init__(self, model):
|
||||||
self._model = model
|
self._model = model
|
||||||
|
|
||||||
@@ -22,13 +24,15 @@ class _FakeSchemaLoader(object):
|
|||||||
return default if type_name in default.types else None
|
return default if type_name in default.types else None
|
||||||
return self._model.namespaces[parts[0]]
|
return self._model.namespaces[parts[0]]
|
||||||
|
|
||||||
|
|
||||||
class CppTypeGeneratorTest(unittest.TestCase):
|
class CppTypeGeneratorTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.models = defaultdict(model.Model)
|
self.models = defaultdict(model.Model)
|
||||||
|
|
||||||
forbidden_json = CachedLoad('test/forbidden.json')
|
forbidden_json = CachedLoad('test/forbidden.json')
|
||||||
self.models['forbidden'].AddNamespace(
|
self.models['forbidden'].AddNamespace(forbidden_json[0],
|
||||||
forbidden_json[0], 'path/to/forbidden.json')
|
'path/to/forbidden.json')
|
||||||
|
|
||||||
permissions_json = CachedLoad('test/permissions.json')
|
permissions_json = CachedLoad('test/permissions.json')
|
||||||
self.permissions = self.models['permissions'].AddNamespace(
|
self.permissions = self.models['permissions'].AddNamespace(
|
||||||
@@ -59,12 +63,13 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
|
|
||||||
objects_movable_idl = idl_schema.Load('test/objects_movable.idl')
|
objects_movable_idl = idl_schema.Load('test/objects_movable.idl')
|
||||||
self.objects_movable = self.models['objects_movable'].AddNamespace(
|
self.objects_movable = self.models['objects_movable'].AddNamespace(
|
||||||
objects_movable_idl[0], 'path/to/objects_movable.idl',
|
objects_movable_idl[0],
|
||||||
|
'path/to/objects_movable.idl',
|
||||||
include_compiler_options=True)
|
include_compiler_options=True)
|
||||||
|
|
||||||
self.simple_api_json = CachedLoad('test/simple_api.json')
|
self.simple_api_json = CachedLoad('test/simple_api.json')
|
||||||
self.models['simple_api'].AddNamespace(
|
self.models['simple_api'].AddNamespace(self.simple_api_json[0],
|
||||||
self.simple_api_json[0], 'path/to/simple_api.json')
|
'path/to/simple_api.json')
|
||||||
|
|
||||||
self.crossref_enums_json = CachedLoad('test/crossref_enums.json')
|
self.crossref_enums_json = CachedLoad('test/crossref_enums.json')
|
||||||
self.crossref_enums = self.models['crossref_enums'].AddNamespace(
|
self.crossref_enums = self.models['crossref_enums'].AddNamespace(
|
||||||
@@ -73,8 +78,7 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
self.crossref_enums_array_json = CachedLoad(
|
self.crossref_enums_array_json = CachedLoad(
|
||||||
'test/crossref_enums_array.json')
|
'test/crossref_enums_array.json')
|
||||||
self.models['crossref_enums_array'].AddNamespace(
|
self.models['crossref_enums_array'].AddNamespace(
|
||||||
self.crossref_enums_array_json[0],
|
self.crossref_enums_array_json[0], 'path/to/crossref_enums_array.json')
|
||||||
'path/to/crossref_enums_array.json')
|
|
||||||
|
|
||||||
def testGenerateIncludesAndForwardDeclarations(self):
|
def testGenerateIncludesAndForwardDeclarations(self):
|
||||||
m = model.Model()
|
m = model.Model()
|
||||||
@@ -88,7 +92,7 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual('', manager.GenerateIncludes().Render())
|
self.assertEqual('', manager.GenerateIncludes().Render())
|
||||||
self.assertEqual('#include "path/to/tabs.h"',
|
self.assertEqual('#include "path/to/tabs.h"',
|
||||||
manager.GenerateIncludes(include_soft=True).Render())
|
manager.GenerateIncludes(include_soft=True).Render())
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'namespace tabs {\n'
|
'namespace tabs {\n'
|
||||||
'struct Tab;\n'
|
'struct Tab;\n'
|
||||||
@@ -96,14 +100,14 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
manager.GenerateForwardDeclarations().Render())
|
manager.GenerateForwardDeclarations().Render())
|
||||||
|
|
||||||
m = model.Model()
|
m = model.Model()
|
||||||
m.AddNamespace(self.windows_json[0],
|
m.AddNamespace(
|
||||||
'path/to/windows.json',
|
self.windows_json[0],
|
||||||
environment=CppNamespaceEnvironment(
|
'path/to/windows.json',
|
||||||
'foo::bar::%(namespace)s'))
|
environment=CppNamespaceEnvironment('foo::bar::%(namespace)s'))
|
||||||
m.AddNamespace(self.tabs_json[0],
|
m.AddNamespace(
|
||||||
'path/to/tabs.json',
|
self.tabs_json[0],
|
||||||
environment=CppNamespaceEnvironment(
|
'path/to/tabs.json',
|
||||||
'foo::bar::%(namespace)s'))
|
environment=CppNamespaceEnvironment('foo::bar::%(namespace)s'))
|
||||||
manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
|
manager = CppTypeGenerator(m, _FakeSchemaLoader(m))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'namespace foo {\n'
|
'namespace foo {\n'
|
||||||
@@ -134,9 +138,10 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
manager = CppTypeGenerator(m,
|
manager = CppTypeGenerator(m,
|
||||||
_FakeSchemaLoader(m),
|
_FakeSchemaLoader(m),
|
||||||
default_namespace=dependency_tester)
|
default_namespace=dependency_tester)
|
||||||
self.assertEqual('#include "path/to/browser_action.h"\n'
|
self.assertEqual(
|
||||||
'#include "path/to/font_settings.h"',
|
'#include "path/to/browser_action.h"\n'
|
||||||
manager.GenerateIncludes().Render())
|
'#include "path/to/font_settings.h"',
|
||||||
|
manager.GenerateIncludes().Render())
|
||||||
self.assertEqual('', manager.GenerateForwardDeclarations().Render())
|
self.assertEqual('', manager.GenerateForwardDeclarations().Render())
|
||||||
|
|
||||||
def testGetCppTypeSimple(self):
|
def testGetCppTypeSimple(self):
|
||||||
@@ -167,7 +172,7 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testGetCppTypeArray(self):
|
def testGetCppTypeArray(self):
|
||||||
manager = CppTypeGenerator(self.models.get('windows'),
|
manager = CppTypeGenerator(self.models.get('windows'),
|
||||||
_FakeSchemaLoader(None))
|
_FakeSchemaLoader(None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'std::vector<Window>',
|
'std::vector<Window>',
|
||||||
manager.GetCppType(
|
manager.GetCppType(
|
||||||
@@ -183,9 +188,8 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
_FakeSchemaLoader(None))
|
_FakeSchemaLoader(None))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'std::vector<MovablePod>',
|
'std::vector<MovablePod>',
|
||||||
manager.GetCppType(
|
manager.GetCppType(self.objects_movable.types['MovableParent'].
|
||||||
self.objects_movable.types['MovableParent'].
|
properties['pods'].type_))
|
||||||
properties['pods'].type_))
|
|
||||||
|
|
||||||
def testGetCppTypeLocalRef(self):
|
def testGetCppTypeLocalRef(self):
|
||||||
manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
|
manager = CppTypeGenerator(self.models.get('tabs'), _FakeSchemaLoader(None))
|
||||||
@@ -211,7 +215,8 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
def testGetCppTypeWithPadForGeneric(self):
|
def testGetCppTypeWithPadForGeneric(self):
|
||||||
manager = CppTypeGenerator(self.models.get('permissions'),
|
manager = CppTypeGenerator(self.models.get('permissions'),
|
||||||
_FakeSchemaLoader(None))
|
_FakeSchemaLoader(None))
|
||||||
self.assertEqual('std::vector<std::string>',
|
self.assertEqual(
|
||||||
|
'std::vector<std::string>',
|
||||||
manager.GetCppType(
|
manager.GetCppType(
|
||||||
self.permissions.types['Permissions'].properties['origins'].type_))
|
self.permissions.types['Permissions'].properties['origins'].type_))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -235,7 +240,7 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
_FakeSchemaLoader(m))
|
_FakeSchemaLoader(m))
|
||||||
|
|
||||||
self.assertEqual('#include "path/to/simple_api.h"',
|
self.assertEqual('#include "path/to/simple_api.h"',
|
||||||
manager.GenerateIncludes().Render())
|
manager.GenerateIncludes().Render())
|
||||||
|
|
||||||
def testHardIncludesForEnumArrays(self):
|
def testHardIncludesForEnumArrays(self):
|
||||||
"""Tests that enums in arrays generate hard includes. Note that it's
|
"""Tests that enums in arrays generate hard includes. Note that it's
|
||||||
@@ -253,7 +258,7 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
_FakeSchemaLoader(m))
|
_FakeSchemaLoader(m))
|
||||||
|
|
||||||
self.assertEqual('#include "path/to/simple_api.h"',
|
self.assertEqual('#include "path/to/simple_api.h"',
|
||||||
manager.GenerateIncludes().Render())
|
manager.GenerateIncludes().Render())
|
||||||
|
|
||||||
def testCrossNamespaceGetEnumDefaultValue(self):
|
def testCrossNamespaceGetEnumDefaultValue(self):
|
||||||
m = model.Model()
|
m = model.Model()
|
||||||
@@ -272,9 +277,9 @@ class CppTypeGeneratorTest(unittest.TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'namespace1::api::simple_api::TestEnum()',
|
'namespace1::api::simple_api::TestEnum()',
|
||||||
manager.GetEnumDefaultValue(
|
manager.GetEnumDefaultValue(
|
||||||
self.crossref_enums.types['CrossrefType']
|
self.crossref_enums.types['CrossrefType'].
|
||||||
.properties['testEnumOptional'].type_,
|
properties['testEnumOptional'].type_, self.crossref_enums))
|
||||||
self.crossref_enums))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# Copyright 2012 The Chromium Authors
|
# Copyright 2012 The Chromium Authors
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
"""Utilies and constants specific to Chromium C++ code.
|
"""Utilies and constants specific to Chromium C++ code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -12,11 +11,9 @@ import os
|
|||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
|
|
||||||
CHROMIUM_LICENSE = (
|
CHROMIUM_LICENSE = ("""// Copyright %d The Chromium Authors
|
||||||
"""// Copyright %d The Chromium Authors
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.""" % datetime.now().year
|
// found in the LICENSE file.""" % datetime.now().year)
|
||||||
)
|
|
||||||
GENERATED_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITION IN
|
GENERATED_FILE_MESSAGE = """// GENERATED FROM THE API DEFINITION IN
|
||||||
// %s
|
// %s
|
||||||
// by tools/json_schema_compiler.
|
// by tools/json_schema_compiler.
|
||||||
@@ -33,6 +30,7 @@ GENERATED_FEATURE_MESSAGE = """// GENERATED FROM THE FEATURE DEFINITIONS IN
|
|||||||
// DO NOT EDIT.
|
// DO NOT EDIT.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def Classname(s):
|
def Classname(s):
|
||||||
"""Translates a namespace name or function name into something more
|
"""Translates a namespace name or function name into something more
|
||||||
suited to C++.
|
suited to C++.
|
||||||
@@ -56,6 +54,7 @@ def Classname(s):
|
|||||||
result = '_' + result
|
result = '_' + result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def GetAsFundamentalValue(type_, src):
|
def GetAsFundamentalValue(type_, src):
|
||||||
"""Returns the C++ code for retrieving a fundamental type from a
|
"""Returns the C++ code for retrieving a fundamental type from a
|
||||||
Value into a variable.
|
Value into a variable.
|
||||||
@@ -68,9 +67,9 @@ def GetAsFundamentalValue(type_, src):
|
|||||||
s = '%s.GetIfDouble()'
|
s = '%s.GetIfDouble()'
|
||||||
elif type_.property_type == PropertyType.INTEGER:
|
elif type_.property_type == PropertyType.INTEGER:
|
||||||
s = '%s.GetIfInt()'
|
s = '%s.GetIfInt()'
|
||||||
elif (type_.property_type == PropertyType.STRING or
|
elif (type_.property_type == PropertyType.STRING
|
||||||
(type_.property_type == PropertyType.FUNCTION and
|
or (type_.property_type == PropertyType.FUNCTION
|
||||||
type_.is_serializable_function)):
|
and type_.is_serializable_function)):
|
||||||
s = '%s.GetIfString()'
|
s = '%s.GetIfString()'
|
||||||
else:
|
else:
|
||||||
raise ValueError('Type %s is not a fundamental value' % type_.name)
|
raise ValueError('Type %s is not a fundamental value' % type_.name)
|
||||||
@@ -104,43 +103,49 @@ def GetValueType(type_):
|
|||||||
|
|
||||||
raise ValueError('Invalid type: %s' % type_.name)
|
raise ValueError('Invalid type: %s' % type_.name)
|
||||||
|
|
||||||
|
|
||||||
def ShouldUseStdOptional(type_):
|
def ShouldUseStdOptional(type_):
|
||||||
"""Called to validate whether or not an optional value should be represented
|
"""Called to validate whether or not an optional value should be represented
|
||||||
with std::optional. This function is a temporary utility, while optional
|
with std::optional. This function is a temporary utility, while optional
|
||||||
fields are gradually migrated away from using std::unique_ptr.
|
fields are gradually migrated away from using std::unique_ptr.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if type_.property_type in (PropertyType.ANY,
|
if type_.property_type in (
|
||||||
PropertyType.ARRAY,
|
PropertyType.ANY,
|
||||||
PropertyType.BINARY,
|
PropertyType.ARRAY,
|
||||||
PropertyType.BOOLEAN,
|
PropertyType.BINARY,
|
||||||
PropertyType.CHOICES,
|
PropertyType.BOOLEAN,
|
||||||
PropertyType.DOUBLE,
|
PropertyType.CHOICES,
|
||||||
PropertyType.FUNCTION,
|
PropertyType.DOUBLE,
|
||||||
PropertyType.INTEGER,
|
PropertyType.FUNCTION,
|
||||||
PropertyType.OBJECT,
|
PropertyType.INTEGER,
|
||||||
PropertyType.STRING):
|
PropertyType.OBJECT,
|
||||||
|
PropertyType.STRING,
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def GetParameterDeclaration(param, type_):
|
def GetParameterDeclaration(param, type_):
|
||||||
"""Gets a parameter declaration of a given model.Property and its C++
|
"""Gets a parameter declaration of a given model.Property and its C++
|
||||||
type.
|
type.
|
||||||
"""
|
"""
|
||||||
if param.type_.property_type in (PropertyType.ANY,
|
if param.type_.property_type in (
|
||||||
PropertyType.ARRAY,
|
PropertyType.ANY,
|
||||||
PropertyType.BINARY,
|
PropertyType.ARRAY,
|
||||||
PropertyType.CHOICES,
|
PropertyType.BINARY,
|
||||||
PropertyType.OBJECT,
|
PropertyType.CHOICES,
|
||||||
PropertyType.REF,
|
PropertyType.OBJECT,
|
||||||
PropertyType.STRING):
|
PropertyType.REF,
|
||||||
|
PropertyType.STRING,
|
||||||
|
):
|
||||||
arg = 'const %(type)s& %(name)s'
|
arg = 'const %(type)s& %(name)s'
|
||||||
else:
|
else:
|
||||||
arg = '%(type)s %(name)s'
|
arg = '%(type)s %(name)s'
|
||||||
return arg % {
|
return arg % {
|
||||||
'type': type_,
|
'type': type_,
|
||||||
'name': param.unix_name,
|
'name': param.unix_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -150,10 +155,10 @@ def GenerateIfndefName(file_path):
|
|||||||
|
|
||||||
e.g chrome/extensions/gen/file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__.
|
e.g chrome/extensions/gen/file.h becomes CHROME_EXTENSIONS_GEN_FILE_H__.
|
||||||
"""
|
"""
|
||||||
return (('%s__' % file_path).upper()
|
return (('%s__' % file_path).upper() \
|
||||||
.replace('\\', '_')
|
.replace('\\', '_') \
|
||||||
.replace('/', '_')
|
.replace('/', '_') \
|
||||||
.replace('-', '_')
|
.replace('-', '_') \
|
||||||
.replace('.', '_'))
|
.replace('.', '_'))
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +185,7 @@ def FeatureNameToConstantName(feature_name):
|
|||||||
"""Returns a kName for a feature's name.
|
"""Returns a kName for a feature's name.
|
||||||
"""
|
"""
|
||||||
return ('k' + ''.join(word[0].upper() + word[1:]
|
return ('k' + ''.join(word[0].upper() + word[1:]
|
||||||
for word in feature_name.replace('.', ' ').split()))
|
for word in feature_name.replace('.', ' ').split()))
|
||||||
|
|
||||||
|
|
||||||
def UnixNameToConstantName(unix_name):
|
def UnixNameToConstantName(unix_name):
|
||||||
@@ -189,6 +194,7 @@ def UnixNameToConstantName(unix_name):
|
|||||||
"""
|
"""
|
||||||
return ('k' + ''.join(word.capitalize() for word in unix_name.split('_')))
|
return ('k' + ''.join(word.capitalize() for word in unix_name.split('_')))
|
||||||
|
|
||||||
|
|
||||||
def IsUnixName(s):
|
def IsUnixName(s):
|
||||||
# type (str) -> bool
|
# type (str) -> bool
|
||||||
"""Returns true if |s| is of the type unix_name i.e. only has lower cased
|
"""Returns true if |s| is of the type unix_name i.e. only has lower cased
|
||||||
@@ -196,6 +202,7 @@ def IsUnixName(s):
|
|||||||
"""
|
"""
|
||||||
return all(x.islower() or x == '_' for x in s) and '_' in s
|
return all(x.islower() or x == '_' for x in s) and '_' in s
|
||||||
|
|
||||||
|
|
||||||
def ToPosixPath(path):
|
def ToPosixPath(path):
|
||||||
"""Returns |path| with separator converted to POSIX style.
|
"""Returns |path| with separator converted to POSIX style.
|
||||||
|
|
||||||
@@ -220,7 +227,7 @@ def GetCppNamespace(pattern, namespace):
|
|||||||
# For some reason Windows builds escape the % characters, so unescape them.
|
# For some reason Windows builds escape the % characters, so unescape them.
|
||||||
# This means that %% can never appear legitimately within a pattern, but
|
# This means that %% can never appear legitimately within a pattern, but
|
||||||
# that's ok. It should never happen.
|
# that's ok. It should never happen.
|
||||||
cpp_namespace = pattern.replace('%%', '%') % { 'namespace': namespace }
|
cpp_namespace = pattern.replace('%%', '%') % {'namespace': namespace}
|
||||||
assert '%' not in cpp_namespace, \
|
assert '%' not in cpp_namespace, \
|
||||||
('Did not manage to fully substitute namespace "%s" into pattern "%s"'
|
('Did not manage to fully substitute namespace "%s" into pattern "%s"'
|
||||||
% (namespace, pattern))
|
% (namespace, pattern))
|
||||||
|
@@ -5,35 +5,27 @@
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from cpp_util import (
|
from cpp_util import (Classname, CloseNamespace, GetCppNamespace,
|
||||||
Classname,
|
GenerateIfndefName, OpenNamespace)
|
||||||
CloseNamespace,
|
|
||||||
GetCppNamespace,
|
|
||||||
GenerateIfndefName,
|
|
||||||
OpenNamespace
|
|
||||||
)
|
|
||||||
|
|
||||||
class CppUtilTest(unittest.TestCase):
|
class CppUtilTest(unittest.TestCase):
|
||||||
|
|
||||||
def testClassname(self):
|
def testClassname(self):
|
||||||
self.assertEqual('Permissions', Classname('permissions'))
|
self.assertEqual('Permissions', Classname('permissions'))
|
||||||
self.assertEqual('UpdateAllTheThings',
|
self.assertEqual('UpdateAllTheThings', Classname('updateAllTheThings'))
|
||||||
Classname('updateAllTheThings'))
|
|
||||||
self.assertEqual('Aa_Bb_Cc', Classname('aa.bb.cc'))
|
self.assertEqual('Aa_Bb_Cc', Classname('aa.bb.cc'))
|
||||||
|
|
||||||
def testNamespaceDeclaration(self):
|
def testNamespaceDeclaration(self):
|
||||||
self.assertEqual('namespace foo {',
|
self.assertEqual('namespace foo {', OpenNamespace('foo').Render())
|
||||||
OpenNamespace('foo').Render())
|
self.assertEqual('} // namespace foo', CloseNamespace('foo').Render())
|
||||||
self.assertEqual('} // namespace foo',
|
|
||||||
CloseNamespace('foo').Render())
|
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual('namespace extensions {\n'
|
||||||
'namespace extensions {\n'
|
'namespace foo {',
|
||||||
'namespace foo {',
|
OpenNamespace('extensions::foo').Render())
|
||||||
OpenNamespace('extensions::foo').Render())
|
self.assertEqual('} // namespace foo\n'
|
||||||
self.assertEqual(
|
'} // namespace extensions',
|
||||||
'} // namespace foo\n'
|
CloseNamespace('extensions::foo').Render())
|
||||||
'} // namespace extensions',
|
|
||||||
CloseNamespace('extensions::foo').Render())
|
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'namespace extensions {\n'
|
'namespace extensions {\n'
|
||||||
|
@@ -72,6 +72,7 @@ CC_FILE_END = """
|
|||||||
} // namespace extensions
|
} // namespace extensions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def ToPosixPath(path):
|
def ToPosixPath(path):
|
||||||
"""Returns |path| with separator converted to POSIX style.
|
"""Returns |path| with separator converted to POSIX style.
|
||||||
|
|
||||||
@@ -79,11 +80,13 @@ def ToPosixPath(path):
|
|||||||
"""
|
"""
|
||||||
return path.replace(os.path.sep, posixpath.sep)
|
return path.replace(os.path.sep, posixpath.sep)
|
||||||
|
|
||||||
|
|
||||||
# Returns true if the list 'l' only contains strings that are a hex-encoded SHA1
|
# Returns true if the list 'l' only contains strings that are a hex-encoded SHA1
|
||||||
# hashes.
|
# hashes.
|
||||||
def ListContainsOnlySha1Hashes(l):
|
def ListContainsOnlySha1Hashes(l):
|
||||||
return len(list(filter(lambda s: not re.match("^[A-F0-9]{40}$", s), l))) == 0
|
return len(list(filter(lambda s: not re.match("^[A-F0-9]{40}$", s), l))) == 0
|
||||||
|
|
||||||
|
|
||||||
# A "grammar" for what is and isn't allowed in the features.json files. This
|
# A "grammar" for what is and isn't allowed in the features.json files. This
|
||||||
# grammar has to list all possible keys and the requirements for each. The
|
# grammar has to list all possible keys and the requirements for each. The
|
||||||
# format of each entry is:
|
# format of each entry is:
|
||||||
@@ -170,19 +173,25 @@ FEATURE_GRAMMAR = ({
|
|||||||
list: {
|
list: {
|
||||||
'enum_map': {
|
'enum_map': {
|
||||||
'privileged_extension':
|
'privileged_extension':
|
||||||
'mojom::ContextType::kPrivilegedExtension',
|
'mojom::ContextType::kPrivilegedExtension',
|
||||||
'privileged_web_page': 'mojom::ContextType::kPrivilegedWebPage',
|
'privileged_web_page':
|
||||||
'content_script': 'mojom::ContextType::kContentScript',
|
'mojom::ContextType::kPrivilegedWebPage',
|
||||||
|
'content_script':
|
||||||
|
'mojom::ContextType::kContentScript',
|
||||||
'lock_screen_extension':
|
'lock_screen_extension':
|
||||||
'mojom::ContextType::kLockscreenExtension',
|
'mojom::ContextType::kLockscreenExtension',
|
||||||
'offscreen_extension':
|
'offscreen_extension':
|
||||||
'mojom::ContextType::kOffscreenExtension',
|
'mojom::ContextType::kOffscreenExtension',
|
||||||
'user_script': 'mojom::ContextType::kUserScript',
|
'user_script':
|
||||||
'web_page': 'mojom::ContextType::kWebPage',
|
'mojom::ContextType::kUserScript',
|
||||||
'webui': 'mojom::ContextType::kWebUi',
|
'web_page':
|
||||||
'webui_untrusted': 'mojom::ContextType::kUntrustedWebUi',
|
'mojom::ContextType::kWebPage',
|
||||||
|
'webui':
|
||||||
|
'mojom::ContextType::kWebUi',
|
||||||
|
'webui_untrusted':
|
||||||
|
'mojom::ContextType::kUntrustedWebUi',
|
||||||
'unprivileged_extension':
|
'unprivileged_extension':
|
||||||
'mojom::ContextType::kUnprivilegedExtension',
|
'mojom::ContextType::kUnprivilegedExtension',
|
||||||
},
|
},
|
||||||
'allow_all': True,
|
'allow_all': True,
|
||||||
'allow_empty': True
|
'allow_empty': True
|
||||||
@@ -210,12 +219,18 @@ FEATURE_GRAMMAR = ({
|
|||||||
'extension_types': {
|
'extension_types': {
|
||||||
list: {
|
list: {
|
||||||
'enum_map': {
|
'enum_map': {
|
||||||
'extension': 'Manifest::TYPE_EXTENSION',
|
'extension':
|
||||||
'hosted_app': 'Manifest::TYPE_HOSTED_APP',
|
'Manifest::TYPE_EXTENSION',
|
||||||
'legacy_packaged_app': 'Manifest::TYPE_LEGACY_PACKAGED_APP',
|
'hosted_app':
|
||||||
'platform_app': 'Manifest::TYPE_PLATFORM_APP',
|
'Manifest::TYPE_HOSTED_APP',
|
||||||
'shared_module': 'Manifest::TYPE_SHARED_MODULE',
|
'legacy_packaged_app':
|
||||||
'theme': 'Manifest::TYPE_THEME',
|
'Manifest::TYPE_LEGACY_PACKAGED_APP',
|
||||||
|
'platform_app':
|
||||||
|
'Manifest::TYPE_PLATFORM_APP',
|
||||||
|
'shared_module':
|
||||||
|
'Manifest::TYPE_SHARED_MODULE',
|
||||||
|
'theme':
|
||||||
|
'Manifest::TYPE_THEME',
|
||||||
'login_screen_extension':
|
'login_screen_extension':
|
||||||
'Manifest::TYPE_LOGIN_SCREEN_EXTENSION',
|
'Manifest::TYPE_LOGIN_SCREEN_EXTENSION',
|
||||||
'chromeos_system_extension':
|
'chromeos_system_extension':
|
||||||
@@ -288,10 +303,12 @@ FEATURE_GRAMMAR = ({
|
|||||||
'session_types': {
|
'session_types': {
|
||||||
list: {
|
list: {
|
||||||
'enum_map': {
|
'enum_map': {
|
||||||
'regular': 'mojom::FeatureSessionType::kRegular',
|
'regular':
|
||||||
'kiosk': 'mojom::FeatureSessionType::kKiosk',
|
'mojom::FeatureSessionType::kRegular',
|
||||||
|
'kiosk':
|
||||||
|
'mojom::FeatureSessionType::kKiosk',
|
||||||
'kiosk.autolaunched':
|
'kiosk.autolaunched':
|
||||||
'mojom::FeatureSessionType::kAutolaunchedKiosk',
|
'mojom::FeatureSessionType::kAutolaunchedKiosk',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -301,21 +318,27 @@ FEATURE_GRAMMAR = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
FEATURE_TYPES = ['APIFeature', 'BehaviorFeature',
|
FEATURE_TYPES = [
|
||||||
'ManifestFeature', 'PermissionFeature']
|
'APIFeature', 'BehaviorFeature', 'ManifestFeature', 'PermissionFeature'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def HasProperty(property_name, value):
|
def HasProperty(property_name, value):
|
||||||
return property_name in value
|
return property_name in value
|
||||||
|
|
||||||
|
|
||||||
def HasAtLeastOneProperty(property_names, value):
|
def HasAtLeastOneProperty(property_names, value):
|
||||||
return any([HasProperty(name, value) for name in property_names])
|
return any([HasProperty(name, value) for name in property_names])
|
||||||
|
|
||||||
|
|
||||||
def DoesNotHaveAllProperties(property_names, value):
|
def DoesNotHaveAllProperties(property_names, value):
|
||||||
return not all([HasProperty(name, value) for name in property_names])
|
return not all([HasProperty(name, value) for name in property_names])
|
||||||
|
|
||||||
|
|
||||||
def DoesNotHaveProperty(property_name, value):
|
def DoesNotHaveProperty(property_name, value):
|
||||||
return property_name not in value
|
return property_name not in value
|
||||||
|
|
||||||
|
|
||||||
def DoesNotHavePropertyInComplexFeature(property_name, feature, all_features):
|
def DoesNotHavePropertyInComplexFeature(property_name, feature, all_features):
|
||||||
if type(feature) is ComplexFeature:
|
if type(feature) is ComplexFeature:
|
||||||
for child_feature in feature.feature_list:
|
for child_feature in feature.feature_list:
|
||||||
@@ -323,6 +346,7 @@ def DoesNotHavePropertyInComplexFeature(property_name, feature, all_features):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def IsEmptyContextsAllowed(feature, all_features):
|
def IsEmptyContextsAllowed(feature, all_features):
|
||||||
# An alias feature wouldn't have the 'contexts' feature value.
|
# An alias feature wouldn't have the 'contexts' feature value.
|
||||||
if feature.GetValue('source'):
|
if feature.GetValue('source'):
|
||||||
@@ -338,12 +362,13 @@ def IsEmptyContextsAllowed(feature, all_features):
|
|||||||
assert contexts, 'contexts must have been specified for the APIFeature'
|
assert contexts, 'contexts must have been specified for the APIFeature'
|
||||||
|
|
||||||
allowlisted_empty_context_namespaces = [
|
allowlisted_empty_context_namespaces = [
|
||||||
'manifestTypes',
|
'manifestTypes',
|
||||||
'extensionsManifestTypes',
|
'extensionsManifestTypes',
|
||||||
'empty_contexts' # Only added for testing.
|
'empty_contexts' # Only added for testing.
|
||||||
]
|
]
|
||||||
return (contexts != '{}' or
|
return (contexts != '{}'
|
||||||
feature.name in allowlisted_empty_context_namespaces)
|
or feature.name in allowlisted_empty_context_namespaces)
|
||||||
|
|
||||||
|
|
||||||
def IsFeatureCrossReference(property_name, reverse_property_name, feature,
|
def IsFeatureCrossReference(property_name, reverse_property_name, feature,
|
||||||
all_features):
|
all_features):
|
||||||
@@ -374,6 +399,7 @@ def IsFeatureCrossReference(property_name, reverse_property_name, feature,
|
|||||||
return True
|
return True
|
||||||
return reverse_reference_value == ('"%s"' % feature.name)
|
return reverse_reference_value == ('"%s"' % feature.name)
|
||||||
|
|
||||||
|
|
||||||
# Verifies that a feature with an allowlist is not available to hosted apps,
|
# Verifies that a feature with an allowlist is not available to hosted apps,
|
||||||
# returning true on success.
|
# returning true on success.
|
||||||
def DoesNotHaveAllowlistForHostedApps(value):
|
def DoesNotHaveAllowlistForHostedApps(value):
|
||||||
@@ -428,92 +454,90 @@ def DoesNotHaveAllowlistForHostedApps(value):
|
|||||||
|
|
||||||
|
|
||||||
SIMPLE_FEATURE_CPP_CLASSES = ({
|
SIMPLE_FEATURE_CPP_CLASSES = ({
|
||||||
'APIFeature': 'SimpleFeature',
|
'APIFeature': 'SimpleFeature',
|
||||||
'ManifestFeature': 'ManifestFeature',
|
'ManifestFeature': 'ManifestFeature',
|
||||||
'PermissionFeature': 'PermissionFeature',
|
'PermissionFeature': 'PermissionFeature',
|
||||||
'BehaviorFeature': 'SimpleFeature',
|
'BehaviorFeature': 'SimpleFeature',
|
||||||
})
|
})
|
||||||
|
|
||||||
VALIDATION = ({
|
VALIDATION = ({
|
||||||
'all': [
|
'all': [
|
||||||
(partial(HasAtLeastOneProperty, ['channel', 'dependencies']),
|
(partial(HasAtLeastOneProperty, ['channel', 'dependencies']),
|
||||||
'Features must specify either a channel or dependencies'),
|
'Features must specify either a channel or dependencies'),
|
||||||
(DoesNotHaveAllowlistForHostedApps,
|
(DoesNotHaveAllowlistForHostedApps,
|
||||||
'Hosted apps are not allowed to use restricted features'),
|
'Hosted apps are not allowed to use restricted features'),
|
||||||
],
|
],
|
||||||
'APIFeature': [
|
'APIFeature':
|
||||||
(partial(HasProperty, 'contexts'),
|
[(partial(HasProperty,
|
||||||
'APIFeatures must specify the contexts property'),
|
'contexts'), 'APIFeatures must specify the contexts property'),
|
||||||
(partial(DoesNotHaveAllProperties, ['alias', 'source']),
|
(partial(DoesNotHaveAllProperties, ['alias', 'source']),
|
||||||
'Features cannot specify both alias and source.')
|
'Features cannot specify both alias and source.')],
|
||||||
],
|
'ManifestFeature': [
|
||||||
'ManifestFeature': [
|
(partial(HasProperty, 'extension_types'),
|
||||||
(partial(HasProperty, 'extension_types'),
|
'ManifestFeatures must specify at least one extension type'),
|
||||||
'ManifestFeatures must specify at least one extension type'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'contexts'),
|
'contexts'), 'ManifestFeatures do not support contexts.'),
|
||||||
'ManifestFeatures do not support contexts.'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'alias'),
|
'alias'), 'ManifestFeatures do not support alias.'),
|
||||||
'ManifestFeatures do not support alias.'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'source'),
|
'source'), 'ManifestFeatures do not support source.'),
|
||||||
'ManifestFeatures do not support source.'),
|
# The `required_buildflags` field is intended to be used to toggle the
|
||||||
# The `required_buildflags` field is intended to be used to toggle the
|
# availability of certain APIs; if we support this for feature types
|
||||||
# availability of certain APIs; if we support this for feature types other
|
# other than APIFeature, we may emit warnings that are visible to
|
||||||
# than APIFeature, we may emit warnings that are visible to developers which
|
# developers which is not desirable.
|
||||||
# is not desirable.
|
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
||||||
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
'ManifestFeatures do not support required_buildflags.'),
|
||||||
'ManifestFeatures do not support required_buildflags.'),
|
],
|
||||||
],
|
'BehaviorFeature': [
|
||||||
'BehaviorFeature': [
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'alias'),
|
'alias'), 'BehaviorFeatures do not support alias.'),
|
||||||
'BehaviorFeatures do not support alias.'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'source'),
|
'source'), 'BehaviorFeatures do not support source.'),
|
||||||
'BehaviorFeatures do not support source.'),
|
# The `required_buildflags` field is intended to be used to toggle the
|
||||||
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
# availability of certain APIs; if we support this for feature types
|
||||||
# The `required_buildflags` field is intended to be used to toggle the
|
# other than APIFeature, we may emit warnings that are visible to
|
||||||
# availability of certain APIs; if we support this for feature types other
|
# developers which is not desirable.
|
||||||
# than APIFeature, we may emit warnings that are visible to developers which
|
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
||||||
# is not desirable.
|
'BehaviorFeatures do not support required_buildflags.'),
|
||||||
'BehaviorFeatures do not support required_buildflags.'),
|
],
|
||||||
],
|
'PermissionFeature': [
|
||||||
'PermissionFeature': [
|
(partial(HasProperty, 'extension_types'),
|
||||||
(partial(HasProperty, 'extension_types'),
|
'PermissionFeatures must specify at least one extension type'),
|
||||||
'PermissionFeatures must specify at least one extension type'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'contexts'),
|
'contexts'), 'PermissionFeatures do not support contexts.'),
|
||||||
'PermissionFeatures do not support contexts.'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'alias'),
|
'alias'), 'PermissionFeatures do not support alias.'),
|
||||||
'PermissionFeatures do not support alias.'),
|
(partial(DoesNotHaveProperty,
|
||||||
(partial(DoesNotHaveProperty, 'source'),
|
'source'), 'PermissionFeatures do not support source.'),
|
||||||
'PermissionFeatures do not support source.'),
|
# The `required_buildflags` field is intended to be used to toggle the
|
||||||
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
# availability of certain APIs; if we support this for feature types
|
||||||
# The `required_buildflags` field is intended to be used to toggle the
|
# other than APIFeature, we may emit warnings that are visible to
|
||||||
# availability of certain APIs; if we support this for feature types other
|
# developers which is not desirable.
|
||||||
# than APIFeature, we may emit warnings that are visible to developers which
|
(partial(DoesNotHaveProperty, 'required_buildflags'),
|
||||||
# is not desirable.
|
'PermissionFeatures do not support required_buildflags.'),
|
||||||
'PermissionFeatures do not support required_buildflags.'),
|
],
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
FINAL_VALIDATION = ({
|
FINAL_VALIDATION = ({
|
||||||
'all': [
|
'all': [
|
||||||
# A complex feature requires at least one child entry at all times; with
|
# A complex feature requires at least one child entry at all times; with
|
||||||
# `required_buildflags` it becomes harder to guarantee that this holds for
|
# `required_buildflags` it becomes harder to guarantee that this holds
|
||||||
# every potential combination of the provided flags.
|
# for every potential combination of the provided flags.
|
||||||
(partial(DoesNotHavePropertyInComplexFeature, 'required_buildflags'),
|
(partial(DoesNotHavePropertyInComplexFeature, 'required_buildflags'),
|
||||||
'required_buildflags cannot be nested in a ComplexFeature'),
|
'required_buildflags cannot be nested in a ComplexFeature'),
|
||||||
],
|
],
|
||||||
'APIFeature': [
|
'APIFeature':
|
||||||
(partial(IsFeatureCrossReference, 'alias', 'source'),
|
[(partial(IsFeatureCrossReference, 'alias', 'source'),
|
||||||
'A feature alias property should reference a feature whose source '
|
'A feature alias property should reference a feature whose source '
|
||||||
'property references it back.'),
|
'property references it back.'),
|
||||||
(partial(IsFeatureCrossReference, 'source', 'alias'),
|
(partial(IsFeatureCrossReference, 'source', 'alias'),
|
||||||
'A feature source property should reference a feature whose alias '
|
'A feature source property should reference a feature whose alias '
|
||||||
'property references it back.'),
|
'property references it back.'),
|
||||||
(IsEmptyContextsAllowed,
|
(IsEmptyContextsAllowed,
|
||||||
'An empty contexts list is not allowed for this feature.')
|
'An empty contexts list is not allowed for this feature.')],
|
||||||
],
|
'ManifestFeature': [],
|
||||||
'ManifestFeature': [],
|
'BehaviorFeature': [],
|
||||||
'BehaviorFeature': [],
|
'PermissionFeature': []
|
||||||
'PermissionFeature': []
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# These keys can not be set on a feature and are hence ignored.
|
# These keys can not be set on a feature and are hence ignored.
|
||||||
@@ -523,6 +547,7 @@ IGNORED_KEYS = ['default_parent', 'required_buildflags']
|
|||||||
# can be disabled for testing.
|
# can be disabled for testing.
|
||||||
ENABLE_ASSERTIONS = True
|
ENABLE_ASSERTIONS = True
|
||||||
|
|
||||||
|
|
||||||
def GetCodeForFeatureValues(feature_values):
|
def GetCodeForFeatureValues(feature_values):
|
||||||
""" Gets the Code object for setting feature values for this object. """
|
""" Gets the Code object for setting feature values for this object. """
|
||||||
c = Code()
|
c = Code()
|
||||||
@@ -533,10 +558,12 @@ def GetCodeForFeatureValues(feature_values):
|
|||||||
c.Append('feature->set_%s(%s);' % (key, feature_values[key]))
|
c.Append('feature->set_%s(%s);' % (key, feature_values[key]))
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
|
||||||
class Feature(object):
|
class Feature(object):
|
||||||
"""A representation of a single simple feature that can handle all parsing,
|
"""A representation of a single simple feature that can handle all parsing,
|
||||||
validation, and code generation.
|
validation, and code generation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.has_parent = False
|
self.has_parent = False
|
||||||
@@ -557,10 +584,10 @@ class Feature(object):
|
|||||||
"""Adds an error relating to a particular key in the feature.
|
"""Adds an error relating to a particular key in the feature.
|
||||||
"""
|
"""
|
||||||
self.AddError('Error parsing feature "%s" at key "%s": %s' %
|
self.AddError('Error parsing feature "%s" at key "%s": %s' %
|
||||||
(self.name, key, error))
|
(self.name, key, error))
|
||||||
|
|
||||||
def _GetCheckedValue(self, key, expected_type, expected_values,
|
def _GetCheckedValue(self, key, expected_type, expected_values, enum_map,
|
||||||
enum_map, value):
|
value):
|
||||||
"""Returns a string to be used in the generated C++ code for a given key's
|
"""Returns a string to be used in the generated C++ code for a given key's
|
||||||
python value, or None if the value is invalid. For example, if the python
|
python value, or None if the value is invalid. For example, if the python
|
||||||
value is True, this returns 'true', for a string foo, this returns "foo",
|
value is True, this returns 'true', for a string foo, this returns "foo",
|
||||||
@@ -737,13 +764,15 @@ class Feature(object):
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
def GetErrors(self):
|
def GetErrors(self):
|
||||||
return self.errors;
|
return self.errors
|
||||||
|
|
||||||
|
|
||||||
class ComplexFeature(Feature):
|
class ComplexFeature(Feature):
|
||||||
""" Complex feature - feature that is comprised of list of features.
|
""" Complex feature - feature that is comprised of list of features.
|
||||||
Overall complex feature is available if any of contained
|
Overall complex feature is available if any of contained
|
||||||
feature is available.
|
feature is available.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
Feature.__init__(self, name)
|
Feature.__init__(self, name)
|
||||||
self.feature_list = []
|
self.feature_list = []
|
||||||
@@ -779,11 +808,13 @@ class ComplexFeature(Feature):
|
|||||||
errors.extend(feature.GetErrors())
|
errors.extend(feature.GetErrors())
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
class FeatureCompiler(object):
|
class FeatureCompiler(object):
|
||||||
"""A compiler to load, parse, and generate C++ code for a number of
|
"""A compiler to load, parse, and generate C++ code for a number of
|
||||||
features.json files."""
|
features.json files."""
|
||||||
def __init__(self, chrome_root, source_files, feature_type,
|
|
||||||
method_name, out_root, gen_dir_relpath, out_base_filename):
|
def __init__(self, chrome_root, source_files, feature_type, method_name,
|
||||||
|
out_root, gen_dir_relpath, out_base_filename):
|
||||||
# See __main__'s ArgumentParser for documentation on these properties.
|
# See __main__'s ArgumentParser for documentation on these properties.
|
||||||
self._chrome_root = chrome_root
|
self._chrome_root = chrome_root
|
||||||
self._source_files = source_files
|
self._source_files = source_files
|
||||||
@@ -808,7 +839,7 @@ class FeatureCompiler(object):
|
|||||||
f_json = json_parse.Parse(f.read())
|
f_json = json_parse.Parse(f.read())
|
||||||
except:
|
except:
|
||||||
print('FAILED: Exception encountered while loading "%s"' %
|
print('FAILED: Exception encountered while loading "%s"' %
|
||||||
abs_source_file)
|
abs_source_file)
|
||||||
raise
|
raise
|
||||||
dupes = set(f_json) & set(self._json)
|
dupes = set(f_json) & set(self._json)
|
||||||
assert not dupes, 'Duplicate keys found: %s' % list(dupes)
|
assert not dupes, 'Duplicate keys found: %s' % list(dupes)
|
||||||
@@ -822,8 +853,8 @@ class FeatureCompiler(object):
|
|||||||
no_parent_values = ['noparent' in v for v in feature_value]
|
no_parent_values = ['noparent' in v for v in feature_value]
|
||||||
no_parent = all(no_parent_values)
|
no_parent = all(no_parent_values)
|
||||||
assert no_parent or not any(no_parent_values), (
|
assert no_parent or not any(no_parent_values), (
|
||||||
'"%s:" All child features must contain the same noparent value' %
|
'"%s:" All child features must contain the same noparent value' %
|
||||||
feature_name)
|
feature_name)
|
||||||
else:
|
else:
|
||||||
no_parent = 'noparent' in feature_value
|
no_parent = 'noparent' in feature_value
|
||||||
sep = feature_name.rfind('.')
|
sep = feature_name.rfind('.')
|
||||||
@@ -889,8 +920,9 @@ class FeatureCompiler(object):
|
|||||||
parse_and_validate(feature_name, v, parent, shared_values))
|
parse_and_validate(feature_name, v, parent, shared_values))
|
||||||
self._features[feature_name] = feature
|
self._features[feature_name] = feature
|
||||||
else:
|
else:
|
||||||
self._features[feature_name] = parse_and_validate(
|
self._features[feature_name] = parse_and_validate(feature_name,
|
||||||
feature_name, feature_value, parent, shared_values)
|
feature_value, parent,
|
||||||
|
shared_values)
|
||||||
|
|
||||||
# Apply parent shared values at the end to enable child features to
|
# Apply parent shared values at the end to enable child features to
|
||||||
# override parent shared value - if parent shared values are added to
|
# override parent shared value - if parent shared values are added to
|
||||||
@@ -926,7 +958,8 @@ class FeatureCompiler(object):
|
|||||||
required_buildflags = feature.GetValue('required_buildflags')
|
required_buildflags = feature.GetValue('required_buildflags')
|
||||||
if required_buildflags:
|
if required_buildflags:
|
||||||
formatted_buildflags = [
|
formatted_buildflags = [
|
||||||
'BUILDFLAG(%s)' % format(flag.upper()) for flag in required_buildflags
|
'BUILDFLAG(%s)' % format(flag.upper())
|
||||||
|
for flag in required_buildflags
|
||||||
]
|
]
|
||||||
c.Append('#if %s' % format(' && '.join(formatted_buildflags)))
|
c.Append('#if %s' % format(' && '.join(formatted_buildflags)))
|
||||||
c.Concat(feature.GetCode(self._feature_type))
|
c.Concat(feature.GetCode(self._feature_type))
|
||||||
@@ -942,16 +975,20 @@ class FeatureCompiler(object):
|
|||||||
header_file = self._out_base_filename + '.h'
|
header_file = self._out_base_filename + '.h'
|
||||||
cc_file = self._out_base_filename + '.cc'
|
cc_file = self._out_base_filename + '.cc'
|
||||||
|
|
||||||
include_file_root = self._out_root[len(self._gen_dir_relpath)+1:]
|
include_file_root = self._out_root[len(self._gen_dir_relpath) + 1:]
|
||||||
header_file_path = '%s/%s' % (include_file_root, header_file)
|
header_file_path = '%s/%s' % (include_file_root, header_file)
|
||||||
cc_file_path = '%s/%s' % (include_file_root, cc_file)
|
cc_file_path = '%s/%s' % (include_file_root, cc_file)
|
||||||
substitutions = ({
|
substitutions = ({
|
||||||
'header_file_path': header_file_path,
|
'header_file_path':
|
||||||
'header_guard': (header_file_path.replace('/', '_').
|
header_file_path,
|
||||||
replace('.', '_').upper()),
|
'header_guard':
|
||||||
'method_name': self._method_name,
|
(header_file_path.replace('/', '_').replace('.', '_').upper()),
|
||||||
'source_files': str([ToPosixPath(f) for f in self._source_files]),
|
'method_name':
|
||||||
'year': str(datetime.now().year)
|
self._method_name,
|
||||||
|
'source_files':
|
||||||
|
str([ToPosixPath(f) for f in self._source_files]),
|
||||||
|
'year':
|
||||||
|
str(datetime.now().year)
|
||||||
})
|
})
|
||||||
if not os.path.exists(self._out_root):
|
if not os.path.exists(self._out_root):
|
||||||
os.makedirs(self._out_root)
|
os.makedirs(self._out_root)
|
||||||
@@ -973,25 +1010,36 @@ class FeatureCompiler(object):
|
|||||||
cc_file.Concat(cc_end)
|
cc_file.Concat(cc_end)
|
||||||
f.write(cc_file.Render().strip())
|
f.write(cc_file.Render().strip())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='Compile json feature files')
|
parser = argparse.ArgumentParser(description='Compile json feature files')
|
||||||
parser.add_argument('chrome_root', type=str,
|
parser.add_argument('chrome_root',
|
||||||
|
type=str,
|
||||||
help='The root directory of the chrome checkout')
|
help='The root directory of the chrome checkout')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'feature_type', type=str,
|
'feature_type',
|
||||||
|
type=str,
|
||||||
help='The name of the class to use in feature generation ' +
|
help='The name of the class to use in feature generation ' +
|
||||||
'(e.g. APIFeature, PermissionFeature)')
|
'(e.g. APIFeature, PermissionFeature)')
|
||||||
parser.add_argument('method_name', type=str,
|
parser.add_argument('method_name',
|
||||||
|
type=str,
|
||||||
help='The name of the method to populate the provider')
|
help='The name of the method to populate the provider')
|
||||||
parser.add_argument('out_root', type=str,
|
parser.add_argument('out_root',
|
||||||
|
type=str,
|
||||||
help='The root directory to generate the C++ files into')
|
help='The root directory to generate the C++ files into')
|
||||||
parser.add_argument('gen_dir_relpath', default='gen', help='Path of the '
|
parser.add_argument(
|
||||||
|
'gen_dir_relpath',
|
||||||
|
default='gen',
|
||||||
|
help='Path of the '
|
||||||
'gen directory relative to the out/. If running in the default '
|
'gen directory relative to the out/. If running in the default '
|
||||||
'toolchain, the path is gen, otherwise $toolchain_name/gen')
|
'toolchain, the path is gen, otherwise $toolchain_name/gen')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'out_base_filename', type=str,
|
'out_base_filename',
|
||||||
|
type=str,
|
||||||
help='The base filename for the C++ files (.h and .cc will be appended)')
|
help='The base filename for the C++ files (.h and .cc will be appended)')
|
||||||
parser.add_argument('source_files', type=str, nargs='+',
|
parser.add_argument('source_files',
|
||||||
|
type=str,
|
||||||
|
nargs='+',
|
||||||
help='The source features.json files')
|
help='The source features.json files')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.feature_type not in FEATURE_TYPES:
|
if args.feature_type not in FEATURE_TYPES:
|
||||||
|
@@ -7,6 +7,7 @@ import copy
|
|||||||
import feature_compiler
|
import feature_compiler
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class FeatureCompilerTest(unittest.TestCase):
|
class FeatureCompilerTest(unittest.TestCase):
|
||||||
"""Test the FeatureCompiler. Note that we test that the expected features are
|
"""Test the FeatureCompiler. Note that we test that the expected features are
|
||||||
generated more thoroughly in features_generation_unittest.cc. And, of course,
|
generated more thoroughly in features_generation_unittest.cc. And, of course,
|
||||||
@@ -14,6 +15,7 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
feature fails to parse, the compile fails).
|
feature fails to parse, the compile fails).
|
||||||
These tests primarily focus on catching errors during parsing.
|
These tests primarily focus on catching errors during parsing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _parseFeature(self, value):
|
def _parseFeature(self, value):
|
||||||
"""Parses a feature from the given value and returns the result."""
|
"""Parses a feature from the given value and returns the result."""
|
||||||
f = feature_compiler.Feature('alpha')
|
f = feature_compiler.Feature('alpha')
|
||||||
@@ -22,7 +24,8 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
|
|
||||||
def _createTestFeatureCompiler(self, feature_class):
|
def _createTestFeatureCompiler(self, feature_class):
|
||||||
return feature_compiler.FeatureCompiler('chrome_root', [], feature_class,
|
return feature_compiler.FeatureCompiler('chrome_root', [], feature_class,
|
||||||
'provider_class', 'out_root', 'gen', 'out_base_filename')
|
'provider_class', 'out_root', 'gen',
|
||||||
|
'out_base_filename')
|
||||||
|
|
||||||
def _hasError(self, f, error):
|
def _hasError(self, f, error):
|
||||||
"""Asserts that |error| is present somewhere in the given feature's
|
"""Asserts that |error| is present somewhere in the given feature's
|
||||||
@@ -37,60 +40,69 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testFeature(self):
|
def testFeature(self):
|
||||||
# Test some basic feature parsing for a sanity check.
|
# Test some basic feature parsing for a sanity check.
|
||||||
f = self._parseFeature({
|
f = self._parseFeature({
|
||||||
'blocklist': [
|
'blocklist': [
|
||||||
'ABCDEF0123456789ABCDEF0123456789ABCDEF01',
|
'ABCDEF0123456789ABCDEF0123456789ABCDEF01',
|
||||||
'10FEDCBA9876543210FEDCBA9876543210FEDCBA'
|
'10FEDCBA9876543210FEDCBA9876543210FEDCBA'
|
||||||
],
|
],
|
||||||
'channel': 'stable',
|
'channel':
|
||||||
'command_line_switch': 'switch',
|
'stable',
|
||||||
'component_extensions_auto_granted': False,
|
'command_line_switch':
|
||||||
'contexts': [
|
'switch',
|
||||||
'privileged_extension',
|
'component_extensions_auto_granted':
|
||||||
'privileged_web_page',
|
False,
|
||||||
'lock_screen_extension'
|
'contexts': [
|
||||||
],
|
'privileged_extension', 'privileged_web_page',
|
||||||
'default_parent': True,
|
'lock_screen_extension'
|
||||||
'dependencies': ['dependency1', 'dependency2'],
|
],
|
||||||
'developer_mode_only': True,
|
'default_parent':
|
||||||
'disallow_for_service_workers': True,
|
True,
|
||||||
'extension_types': ['extension'],
|
'dependencies': ['dependency1', 'dependency2'],
|
||||||
'location': 'component',
|
'developer_mode_only':
|
||||||
'internal': True,
|
True,
|
||||||
'matches': ['*://*/*'],
|
'disallow_for_service_workers':
|
||||||
'max_manifest_version': 1,
|
True,
|
||||||
'requires_delegated_availability_check': True,
|
'extension_types': ['extension'],
|
||||||
'noparent': True,
|
'location':
|
||||||
'platforms': ['mac', 'win'],
|
'component',
|
||||||
'session_types': ['kiosk', 'regular'],
|
'internal':
|
||||||
'allowlist': [
|
True,
|
||||||
'0123456789ABCDEF0123456789ABCDEF01234567',
|
'matches': ['*://*/*'],
|
||||||
'76543210FEDCBA9876543210FEDCBA9876543210'
|
'max_manifest_version':
|
||||||
],
|
1,
|
||||||
'required_buildflags': [
|
'requires_delegated_availability_check':
|
||||||
'use_cups'
|
True,
|
||||||
]
|
'noparent':
|
||||||
|
True,
|
||||||
|
'platforms': ['mac', 'win'],
|
||||||
|
'session_types': ['kiosk', 'regular'],
|
||||||
|
'allowlist': [
|
||||||
|
'0123456789ABCDEF0123456789ABCDEF01234567',
|
||||||
|
'76543210FEDCBA9876543210FEDCBA9876543210'
|
||||||
|
],
|
||||||
|
'required_buildflags': ['use_cups']
|
||||||
})
|
})
|
||||||
self.assertFalse(f.GetErrors())
|
self.assertFalse(f.GetErrors())
|
||||||
|
|
||||||
def testInvalidAll(self):
|
def testInvalidAll(self):
|
||||||
f = self._parseFeature({
|
f = self._parseFeature({
|
||||||
'channel': 'stable',
|
'channel': 'stable',
|
||||||
'dependencies': 'all',
|
'dependencies': 'all',
|
||||||
})
|
})
|
||||||
self._hasError(f, 'Illegal value: "all"')
|
self._hasError(f, 'Illegal value: "all"')
|
||||||
|
|
||||||
def testUnknownKeyError(self):
|
def testUnknownKeyError(self):
|
||||||
f = self._parseFeature({
|
f = self._parseFeature({
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'stable',
|
'channel': 'stable',
|
||||||
'unknownkey': 'unknownvalue'
|
'unknownkey': 'unknownvalue'
|
||||||
})
|
})
|
||||||
self._hasError(f, 'Unrecognized key')
|
self._hasError(f, 'Unrecognized key')
|
||||||
|
|
||||||
def testUnknownEnumValue(self):
|
def testUnknownEnumValue(self):
|
||||||
f = self._parseFeature({
|
f = self._parseFeature({
|
||||||
'contexts': ['privileged_extension', 'unknown_context'],
|
'contexts': ['privileged_extension', 'unknown_context'],
|
||||||
'channel': 'stable'
|
'channel':
|
||||||
|
'stable'
|
||||||
})
|
})
|
||||||
self._hasError(f, 'Illegal value: "unknown_context"')
|
self._hasError(f, 'Illegal value: "unknown_context"')
|
||||||
|
|
||||||
@@ -116,15 +128,19 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
self.assertFalse(f.GetErrors())
|
self.assertFalse(f.GetErrors())
|
||||||
|
|
||||||
def testApiFeaturesNeedContexts(self):
|
def testApiFeaturesNeedContexts(self):
|
||||||
f = self._parseFeature({'extension_types': ['extension'],
|
f = self._parseFeature({
|
||||||
'channel': 'trunk'})
|
'extension_types': ['extension'],
|
||||||
|
'channel': 'trunk'
|
||||||
|
})
|
||||||
f.Validate('APIFeature', {})
|
f.Validate('APIFeature', {})
|
||||||
self._hasError(f, 'APIFeatures must specify the contexts property')
|
self._hasError(f, 'APIFeatures must specify the contexts property')
|
||||||
|
|
||||||
def testAPIFeaturesCanSpecifyEmptyContexts(self):
|
def testAPIFeaturesCanSpecifyEmptyContexts(self):
|
||||||
f = self._parseFeature({'extension_types': ['extension'],
|
f = self._parseFeature({
|
||||||
'channel': 'trunk',
|
'extension_types': ['extension'],
|
||||||
'contexts': []})
|
'channel': 'trunk',
|
||||||
|
'contexts': []
|
||||||
|
})
|
||||||
f.Validate('APIFeature', {})
|
f.Validate('APIFeature', {})
|
||||||
self.assertFalse(f.GetErrors())
|
self.assertFalse(f.GetErrors())
|
||||||
|
|
||||||
@@ -135,10 +151,12 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
'ManifestFeatures must specify at least one extension type')
|
'ManifestFeatures must specify at least one extension type')
|
||||||
|
|
||||||
def testManifestFeaturesCantHaveContexts(self):
|
def testManifestFeaturesCantHaveContexts(self):
|
||||||
f = self._parseFeature({'dependencies': 'alpha',
|
f = self._parseFeature({
|
||||||
'channel': 'beta',
|
'dependencies': 'alpha',
|
||||||
'extension_types': ['extension'],
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension']})
|
'extension_types': ['extension'],
|
||||||
|
'contexts': ['privileged_extension']
|
||||||
|
})
|
||||||
f.Validate('ManifestFeature', {})
|
f.Validate('ManifestFeature', {})
|
||||||
self._hasError(f, 'ManifestFeatures do not support contexts')
|
self._hasError(f, 'ManifestFeatures do not support contexts')
|
||||||
|
|
||||||
@@ -149,18 +167,20 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
f, 'PermissionFeatures must specify at least one extension type')
|
f, 'PermissionFeatures must specify at least one extension type')
|
||||||
|
|
||||||
def testPermissionFeaturesCantHaveContexts(self):
|
def testPermissionFeaturesCantHaveContexts(self):
|
||||||
f = self._parseFeature({'dependencies': 'alpha',
|
f = self._parseFeature({
|
||||||
'channel': 'beta',
|
'dependencies': 'alpha',
|
||||||
'extension_types': ['extension'],
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension']})
|
'extension_types': ['extension'],
|
||||||
|
'contexts': ['privileged_extension']
|
||||||
|
})
|
||||||
f.Validate('PermissionFeature', {})
|
f.Validate('PermissionFeature', {})
|
||||||
self._hasError(f, 'PermissionFeatures do not support contexts')
|
self._hasError(f, 'PermissionFeatures do not support contexts')
|
||||||
|
|
||||||
def testAllPermissionsNeedChannelOrDependencies(self):
|
def testAllPermissionsNeedChannelOrDependencies(self):
|
||||||
api_feature = self._parseFeature({'contexts': ['privileged_extension']})
|
api_feature = self._parseFeature({'contexts': ['privileged_extension']})
|
||||||
api_feature.Validate('APIFeature', {})
|
api_feature.Validate('APIFeature', {})
|
||||||
self._hasError(
|
self._hasError(api_feature,
|
||||||
api_feature, 'Features must specify either a channel or dependencies')
|
'Features must specify either a channel or dependencies')
|
||||||
permission_feature = self._parseFeature({'extension_types': ['extension']})
|
permission_feature = self._parseFeature({'extension_types': ['extension']})
|
||||||
permission_feature.Validate('PermissionFeature', {})
|
permission_feature.Validate('PermissionFeature', {})
|
||||||
self._hasError(permission_feature,
|
self._hasError(permission_feature,
|
||||||
@@ -169,25 +189,28 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
manifest_feature.Validate('ManifestFeature', {})
|
manifest_feature.Validate('ManifestFeature', {})
|
||||||
self._hasError(manifest_feature,
|
self._hasError(manifest_feature,
|
||||||
'Features must specify either a channel or dependencies')
|
'Features must specify either a channel or dependencies')
|
||||||
channel_feature = self._parseFeature({'contexts': ['privileged_extension'],
|
channel_feature = self._parseFeature({
|
||||||
'channel': 'trunk'})
|
'contexts': ['privileged_extension'],
|
||||||
|
'channel': 'trunk'
|
||||||
|
})
|
||||||
channel_feature.Validate('APIFeature', {})
|
channel_feature.Validate('APIFeature', {})
|
||||||
self.assertFalse(channel_feature.GetErrors())
|
self.assertFalse(channel_feature.GetErrors())
|
||||||
dependency_feature = self._parseFeature(
|
dependency_feature = self._parseFeature({
|
||||||
{'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'dependencies': ['alpha']})
|
'dependencies': ['alpha']
|
||||||
|
})
|
||||||
dependency_feature.Validate('APIFeature', {})
|
dependency_feature.Validate('APIFeature', {})
|
||||||
self.assertFalse(dependency_feature.GetErrors())
|
self.assertFalse(dependency_feature.GetErrors())
|
||||||
|
|
||||||
def testBothAliasAndSource(self):
|
def testBothAliasAndSource(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_alpha',
|
'alias': 'feature_alpha',
|
||||||
'source': 'feature_alpha'
|
'source': 'feature_alpha'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
@@ -198,20 +221,20 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testAliasOnNonApiFeature(self):
|
def testAliasOnNonApiFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('PermissionFeature')
|
compiler = self._createTestFeatureCompiler('PermissionFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
},
|
},
|
||||||
'feature_beta': [{
|
'feature_beta': [{
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'source': 'feature_alpha'
|
'source': 'feature_alpha'
|
||||||
},{
|
}, {
|
||||||
'channel': 'dev',
|
'channel': 'dev',
|
||||||
'context': ['privileged_extension']
|
'context': ['privileged_extension']
|
||||||
}]
|
}]
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
@@ -225,17 +248,17 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testAliasFeature(self):
|
def testAliasFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
},
|
},
|
||||||
'feature_beta': {
|
'feature_beta': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'source': 'feature_alpha'
|
'source': 'feature_alpha'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
@@ -249,40 +272,41 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testMultipleAliasesInComplexFeature(self):
|
def testMultipleAliasesInComplexFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': [{
|
'feature_alpha': [{
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
}, {
|
}, {
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
}]
|
}]
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature, 'Error parsing feature "feature_alpha" at key ' +
|
self._hasError(
|
||||||
'"alias": Key can be set at most once per feature.')
|
feature, 'Error parsing feature "feature_alpha" at key ' +
|
||||||
|
'"alias": Key can be set at most once per feature.')
|
||||||
|
|
||||||
def testAliasReferenceInComplexFeature(self):
|
def testAliasReferenceInComplexFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': [{
|
'feature_alpha': [{
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
}, {
|
}, {
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
}],
|
}],
|
||||||
'feature_beta': {
|
'feature_beta': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'source': 'feature_alpha'
|
'source': 'feature_alpha'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
@@ -296,57 +320,58 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testSourceMissingReference(self):
|
def testSourceMissingReference(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
},
|
},
|
||||||
'feature_beta': {
|
'feature_beta': {
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'source': 'does_not_exist'
|
'source': 'does_not_exist'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_beta')
|
feature = compiler._features.get('feature_beta')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature, 'A feature source property should reference a ' +
|
self._hasError(
|
||||||
'feature whose alias property references it back.')
|
feature, 'A feature source property should reference a ' +
|
||||||
|
'feature whose alias property references it back.')
|
||||||
|
|
||||||
def testAliasMissingReferenceInComplexFeature(self):
|
def testAliasMissingReferenceInComplexFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': [{
|
'feature_alpha': [{
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_beta'
|
'alias': 'feature_beta'
|
||||||
}, {
|
}, {
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'beta'
|
'channel': 'beta'
|
||||||
}]
|
}]
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature, 'A feature alias property should reference a ' +
|
self._hasError(
|
||||||
'feature whose source property references it back.')
|
feature, 'A feature alias property should reference a ' +
|
||||||
|
'feature whose source property references it back.')
|
||||||
|
|
||||||
def testAliasReferenceMissingSourceInComplexFeature(self):
|
def testAliasReferenceMissingSourceInComplexFeature(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
},
|
},
|
||||||
'feature_beta': {
|
'feature_beta': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'alias': 'feature_alpha'
|
'alias': 'feature_alpha'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
@@ -355,23 +380,23 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
|
|
||||||
feature = compiler._features.get('feature_beta')
|
feature = compiler._features.get('feature_beta')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature, 'A feature alias property should reference a ' +
|
self._hasError(
|
||||||
'feature whose source property references it back.')
|
feature, 'A feature alias property should reference a ' +
|
||||||
|
'feature whose source property references it back.')
|
||||||
|
|
||||||
def testComplexParentWithoutDefaultParent(self):
|
def testComplexParentWithoutDefaultParent(self):
|
||||||
c = feature_compiler.FeatureCompiler(
|
c = feature_compiler.FeatureCompiler(None, None, 'APIFeature', None, None,
|
||||||
None, None, 'APIFeature', None, None, None, None)
|
None, None)
|
||||||
c._CompileFeature('bookmarks',
|
c._CompileFeature('bookmarks', [{
|
||||||
[{
|
'contexts': ['privileged_extension'],
|
||||||
'contexts': ['privileged_extension'],
|
}, {
|
||||||
}, {
|
'channel': 'stable',
|
||||||
'channel': 'stable',
|
'contexts': ['webui'],
|
||||||
'contexts': ['webui'],
|
}])
|
||||||
}])
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(AssertionError,
|
with self.assertRaisesRegex(AssertionError,
|
||||||
'No default parent found for bookmarks'):
|
'No default parent found for bookmarks'):
|
||||||
c._CompileFeature('bookmarks.export', { "allowlist": ["asdf"] })
|
c._CompileFeature('bookmarks.export', {"allowlist": ["asdf"]})
|
||||||
|
|
||||||
def testComplexFeatureWithSinglePropertyBlock(self):
|
def testComplexFeatureWithSinglePropertyBlock(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
@@ -383,16 +408,18 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
'feature key instead of a list.')
|
'feature key instead of a list.')
|
||||||
with self.assertRaisesRegex(AssertionError, error):
|
with self.assertRaisesRegex(AssertionError, error):
|
||||||
compiler._CompileFeature('feature_alpha',
|
compiler._CompileFeature('feature_alpha',
|
||||||
[{
|
[{
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'channel': 'stable',
|
'channel': 'stable',
|
||||||
}])
|
}])
|
||||||
|
|
||||||
def testRealIdsDisallowedInAllowlist(self):
|
def testRealIdsDisallowedInAllowlist(self):
|
||||||
fake_id = 'a' * 32;
|
fake_id = 'a' * 32
|
||||||
f = self._parseFeature({'allowlist': [fake_id],
|
f = self._parseFeature({
|
||||||
'extension_types': ['extension'],
|
'allowlist': [fake_id],
|
||||||
'channel': 'beta'})
|
'extension_types': ['extension'],
|
||||||
|
'channel': 'beta'
|
||||||
|
})
|
||||||
f.Validate('PermissionFeature', {})
|
f.Validate('PermissionFeature', {})
|
||||||
self._hasError(
|
self._hasError(
|
||||||
f, 'list should only have hex-encoded SHA1 hashes of extension ids')
|
f, 'list should only have hex-encoded SHA1 hashes of extension ids')
|
||||||
@@ -401,31 +428,34 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
f = self._parseFeature({
|
f = self._parseFeature({
|
||||||
'extension_types': ['extension', 'hosted_app'],
|
'extension_types': ['extension', 'hosted_app'],
|
||||||
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
||||||
'channel': 'beta',
|
'channel':
|
||||||
|
'beta',
|
||||||
})
|
})
|
||||||
f.Validate('PermissionFeature', {})
|
f.Validate('PermissionFeature', {})
|
||||||
self._hasError(f, 'Hosted apps are not allowed to use restricted features')
|
self._hasError(f, 'Hosted apps are not allowed to use restricted features')
|
||||||
|
|
||||||
def testHostedAppsCantUseAllowlistedFeatures_ComplexFeature(self):
|
def testHostedAppsCantUseAllowlistedFeatures_ComplexFeature(self):
|
||||||
c = feature_compiler.FeatureCompiler(
|
c = feature_compiler.FeatureCompiler(None, None, 'PermissionFeature', None,
|
||||||
None, None, 'PermissionFeature', None, None, None, None)
|
None, None, None)
|
||||||
c._CompileFeature('invalid_feature',
|
c._CompileFeature(
|
||||||
|
'invalid_feature',
|
||||||
[{
|
[{
|
||||||
'extension_types': ['extension'],
|
'extension_types': ['extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
}, {
|
}, {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'extension_types': ['hosted_app'],
|
'extension_types': ['hosted_app'],
|
||||||
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
||||||
}])
|
}])
|
||||||
c._CompileFeature('valid_feature',
|
c._CompileFeature(
|
||||||
|
'valid_feature',
|
||||||
[{
|
[{
|
||||||
'extension_types': ['extension'],
|
'extension_types': ['extension'],
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567'],
|
||||||
}, {
|
}, {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'extension_types': ['hosted_app'],
|
'extension_types': ['hosted_app'],
|
||||||
}])
|
}])
|
||||||
|
|
||||||
valid_feature = c._features.get('valid_feature')
|
valid_feature = c._features.get('valid_feature')
|
||||||
@@ -437,20 +467,17 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
self._hasError(invalid_feature,
|
self._hasError(invalid_feature,
|
||||||
'Hosted apps are not allowed to use restricted features')
|
'Hosted apps are not allowed to use restricted features')
|
||||||
|
|
||||||
|
|
||||||
def testHostedAppsCantUseAllowlistedFeatures_ChildFeature(self):
|
def testHostedAppsCantUseAllowlistedFeatures_ChildFeature(self):
|
||||||
c = feature_compiler.FeatureCompiler(
|
c = feature_compiler.FeatureCompiler(None, None, 'PermissionFeature', None,
|
||||||
None, None, 'PermissionFeature', None, None, None, None)
|
None, None, None)
|
||||||
c._CompileFeature('parent',
|
c._CompileFeature('parent', {
|
||||||
{
|
'extension_types': ['hosted_app'],
|
||||||
'extension_types': ['hosted_app'],
|
'channel': 'beta',
|
||||||
'channel': 'beta',
|
})
|
||||||
})
|
|
||||||
|
|
||||||
c._CompileFeature('parent.child',
|
c._CompileFeature(
|
||||||
{
|
'parent.child',
|
||||||
'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567']
|
{'allowlist': ['0123456789ABCDEF0123456789ABCDEF01234567']})
|
||||||
})
|
|
||||||
feature = c._features.get('parent.child')
|
feature = c._features.get('parent.child')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature,
|
self._hasError(feature,
|
||||||
@@ -459,27 +486,27 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
def testEmptyContextsDisallowed(self):
|
def testEmptyContextsDisallowed(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_alpha': {
|
'feature_alpha': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': [],
|
'contexts': [],
|
||||||
'extension_types': ['extension']
|
'extension_types': ['extension']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
feature = compiler._features.get('feature_alpha')
|
feature = compiler._features.get('feature_alpha')
|
||||||
self.assertTrue(feature)
|
self.assertTrue(feature)
|
||||||
self._hasError(feature,
|
self._hasError(feature,
|
||||||
'An empty contexts list is not allowed for this feature.')
|
'An empty contexts list is not allowed for this feature.')
|
||||||
|
|
||||||
def testEmptyContextsAllowed(self):
|
def testEmptyContextsAllowed(self):
|
||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'empty_contexts': {
|
'empty_contexts': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': [],
|
'contexts': [],
|
||||||
'extension_types': ['extension']
|
'extension_types': ['extension']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
|
|
||||||
@@ -491,18 +518,19 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
compiler = self._createTestFeatureCompiler('APIFeature')
|
compiler = self._createTestFeatureCompiler('APIFeature')
|
||||||
|
|
||||||
compiler._json = {
|
compiler._json = {
|
||||||
'feature_cups': {
|
'feature_cups': {
|
||||||
'channel': 'beta',
|
'channel': 'beta',
|
||||||
'contexts': ['privileged_extension'],
|
'contexts': ['privileged_extension'],
|
||||||
'extension_types': ['extension'],
|
'extension_types': ['extension'],
|
||||||
'required_buildflags': ['use_cups']
|
'required_buildflags': ['use_cups']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compiler.Compile()
|
compiler.Compile()
|
||||||
cc_code = compiler.Render()
|
cc_code = compiler.Render()
|
||||||
|
|
||||||
# The code below is formatted correctly!
|
# The code below is formatted correctly!
|
||||||
self.assertEqual(cc_code.Render(), ''' {
|
self.assertEqual(
|
||||||
|
cc_code.Render(), ''' {
|
||||||
#if BUILDFLAG(USE_CUPS)
|
#if BUILDFLAG(USE_CUPS)
|
||||||
SimpleFeature* feature = new SimpleFeature();
|
SimpleFeature* feature = new SimpleFeature();
|
||||||
feature->set_name("feature_cups");
|
feature->set_name("feature_cups");
|
||||||
@@ -513,5 +541,6 @@ class FeatureCompilerTest(unittest.TestCase):
|
|||||||
#endif
|
#endif
|
||||||
}''')
|
}''')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@@ -9,6 +9,7 @@ import cpp_util
|
|||||||
|
|
||||||
|
|
||||||
class CCGenerator(object):
|
class CCGenerator(object):
|
||||||
|
|
||||||
def Generate(self, feature_defs, source_file, namespace):
|
def Generate(self, feature_defs, source_file, namespace):
|
||||||
return _Generator(feature_defs, source_file, namespace).Generate()
|
return _Generator(feature_defs, source_file, namespace).Generate()
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ class CCGenerator(object):
|
|||||||
class _Generator(object):
|
class _Generator(object):
|
||||||
"""A .cc generator for features.
|
"""A .cc generator for features.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, feature_defs, source_file, namespace):
|
def __init__(self, feature_defs, source_file, namespace):
|
||||||
self._feature_defs = feature_defs
|
self._feature_defs = feature_defs
|
||||||
self._source_file = source_file
|
self._source_file = source_file
|
||||||
@@ -27,54 +29,53 @@ class _Generator(object):
|
|||||||
"""Generates a Code object for features.
|
"""Generates a Code object for features.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append(cpp_util.CHROMIUM_LICENSE)
|
(c.Append(cpp_util.CHROMIUM_LICENSE) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append(cpp_util.GENERATED_FEATURE_MESSAGE %
|
.Append(cpp_util.GENERATED_FEATURE_MESSAGE %
|
||||||
cpp_util.ToPosixPath(self._source_file))
|
cpp_util.ToPosixPath(self._source_file)) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include <string>')
|
.Append('#include <string>') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include "%s.h"' %
|
.Append('#include "%s.h"' %
|
||||||
cpp_util.ToPosixPath(self._source_file_filename))
|
cpp_util.ToPosixPath(self._source_file_filename)) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include "base/notreached.h"')
|
.Append('#include "base/notreached.h"') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Concat(cpp_util.OpenNamespace(self._namespace))
|
.Concat(cpp_util.OpenNamespace(self._namespace)) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate the constructor.
|
# Generate the constructor.
|
||||||
(c.Append('%s::%s() {' % (self._class_name, self._class_name))
|
(c.Append('%s::%s() {' % (self._class_name, self._class_name)) \
|
||||||
.Sblock()
|
.Sblock()
|
||||||
)
|
)
|
||||||
for feature in self._feature_defs:
|
for feature in self._feature_defs:
|
||||||
c.Append('features_["%s"] = %s;'
|
c.Append('features_["%s"] = %s;' %
|
||||||
% (feature.name,
|
(feature.name, cpp_util.FeatureNameToConstantName(feature.name)))
|
||||||
cpp_util.FeatureNameToConstantName(feature.name)))
|
(c.Eblock() \
|
||||||
(c.Eblock()
|
.Append('}') \
|
||||||
.Append('}')
|
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate the ToString function.
|
# Generate the ToString function.
|
||||||
(c.Append('const char* %s::ToString('
|
(c.Append('const char* %s::ToString('
|
||||||
'%s::ID id) const {' % (self._class_name, self._class_name))
|
'%s::ID id) const {' % (self._class_name, self._class_name)) \
|
||||||
.Sblock()
|
.Sblock() \
|
||||||
.Append('switch (id) {')
|
.Append('switch (id) {') \
|
||||||
.Sblock()
|
.Sblock()
|
||||||
)
|
)
|
||||||
for feature in self._feature_defs:
|
for feature in self._feature_defs:
|
||||||
c.Append('case %s: return "%s";' %
|
c.Append('case %s: return "%s";' %
|
||||||
(cpp_util.FeatureNameToConstantName(feature.name), feature.name))
|
(cpp_util.FeatureNameToConstantName(feature.name), feature.name))
|
||||||
(c.Append('case kUnknown: break;')
|
(c.Append('case kUnknown: break;') \
|
||||||
.Append('case kEnumBoundary: break;')
|
.Append('case kEnumBoundary: break;') \
|
||||||
.Eblock()
|
.Eblock() \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('NOTREACHED_IN_MIGRATION();')
|
.Append('NOTREACHED_IN_MIGRATION();') \
|
||||||
.Append('return "";')
|
.Append('return "";')
|
||||||
)
|
)
|
||||||
(c.Eblock()
|
(c.Eblock() \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,13 +83,13 @@ class _Generator(object):
|
|||||||
|
|
||||||
(c.Append('%s::ID %s::FromString('
|
(c.Append('%s::ID %s::FromString('
|
||||||
'const std::string& id) const {'
|
'const std::string& id) const {'
|
||||||
% (self._class_name, self._class_name))
|
% (self._class_name, self._class_name)) \
|
||||||
.Sblock()
|
.Sblock() \
|
||||||
.Append('const auto& it = features_.find(id);' % self._class_name)
|
.Append('const auto& it = features_.find(id);' % self._class_name) \
|
||||||
.Append('return (it == features_.end()) ? kUnknown : it->second;')
|
.Append('return (it == features_.end()) ? kUnknown : it->second;') \
|
||||||
.Eblock()
|
.Eblock() \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Cblock(cpp_util.CloseNamespace(self._namespace))
|
.Cblock(cpp_util.CloseNamespace(self._namespace))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -23,9 +23,7 @@ def _GenerateSchema(filename, root, destdir, namespace):
|
|||||||
# Load in the feature permissions from the JSON file.
|
# Load in the feature permissions from the JSON file.
|
||||||
schema = os.path.normpath(filename)
|
schema = os.path.normpath(filename)
|
||||||
schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(schema, root)),
|
schema_loader = SchemaLoader(os.path.dirname(os.path.relpath(schema, root)),
|
||||||
os.path.dirname(schema),
|
os.path.dirname(schema), [], None)
|
||||||
[],
|
|
||||||
None)
|
|
||||||
schema_filename = os.path.splitext(schema)[0]
|
schema_filename = os.path.splitext(schema)[0]
|
||||||
feature_defs = schema_loader.LoadSchema(schema)
|
feature_defs = schema_loader.LoadSchema(schema)
|
||||||
|
|
||||||
@@ -38,10 +36,8 @@ def _GenerateSchema(filename, root, destdir, namespace):
|
|||||||
relpath = os.path.relpath(os.path.normpath(source_file_dir), root)
|
relpath = os.path.relpath(os.path.normpath(source_file_dir), root)
|
||||||
full_path = os.path.join(relpath, schema)
|
full_path = os.path.join(relpath, schema)
|
||||||
|
|
||||||
generators = [
|
generators = [('%s.cc' % schema_filename, CCGenerator()),
|
||||||
('%s.cc' % schema_filename, CCGenerator()),
|
('%s.h' % schema_filename, HGenerator())]
|
||||||
('%s.h' % schema_filename, HGenerator())
|
|
||||||
]
|
|
||||||
|
|
||||||
# Generate and output the code for all features.
|
# Generate and output the code for all features.
|
||||||
output_code = []
|
output_code = []
|
||||||
@@ -59,12 +55,19 @@ if __name__ == '__main__':
|
|||||||
parser = optparse.OptionParser(
|
parser = optparse.OptionParser(
|
||||||
description='Generates a C++ features model from JSON schema',
|
description='Generates a C++ features model from JSON schema',
|
||||||
usage='usage: %prog [option]... schema')
|
usage='usage: %prog [option]... schema')
|
||||||
parser.add_option('-r', '--root', default='.',
|
parser.add_option(
|
||||||
|
'-r',
|
||||||
|
'--root',
|
||||||
|
default='.',
|
||||||
help='logical include root directory. Path to schema files from '
|
help='logical include root directory. Path to schema files from '
|
||||||
'specified dir will be the include path.')
|
'specified dir will be the include path.')
|
||||||
parser.add_option('-d', '--destdir',
|
parser.add_option('-d',
|
||||||
help='root directory to output generated files.')
|
'--destdir',
|
||||||
parser.add_option('-n', '--namespace', default='generated_features',
|
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.')
|
help='C++ namespace for generated files. e.g extensions::api.')
|
||||||
(opts, filenames) = parser.parse_args()
|
(opts, filenames) = parser.parse_args()
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@ import cpp_util
|
|||||||
|
|
||||||
|
|
||||||
class HGenerator(object):
|
class HGenerator(object):
|
||||||
|
|
||||||
def Generate(self, features, source_file, namespace):
|
def Generate(self, features, source_file, namespace):
|
||||||
return _Generator(features, source_file, namespace).Generate()
|
return _Generator(features, source_file, namespace).Generate()
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ class HGenerator(object):
|
|||||||
class _Generator(object):
|
class _Generator(object):
|
||||||
"""A .cc generator for features.
|
"""A .cc generator for features.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, features, source_file, namespace):
|
def __init__(self, features, source_file, namespace):
|
||||||
self._feature_defs = features
|
self._feature_defs = features
|
||||||
self._source_file = source_file
|
self._source_file = source_file
|
||||||
@@ -27,10 +29,10 @@ class _Generator(object):
|
|||||||
"""Generates a Code object for features.
|
"""Generates a Code object for features.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append(cpp_util.CHROMIUM_LICENSE)
|
(c.Append(cpp_util.CHROMIUM_LICENSE) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append(cpp_util.GENERATED_FEATURE_MESSAGE %
|
.Append(cpp_util.GENERATED_FEATURE_MESSAGE %
|
||||||
cpp_util.ToPosixPath(self._source_file))
|
cpp_util.ToPosixPath(self._source_file)) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,31 +41,31 @@ class _Generator(object):
|
|||||||
output_file = os.path.splitext(self._namespace.source_file)[0] + '.h'
|
output_file = os.path.splitext(self._namespace.source_file)[0] + '.h'
|
||||||
ifndef_name = cpp_util.GenerateIfndefName(output_file)
|
ifndef_name = cpp_util.GenerateIfndefName(output_file)
|
||||||
|
|
||||||
(c.Append('#ifndef %s' % ifndef_name)
|
(c.Append('#ifndef %s' % ifndef_name) \
|
||||||
.Append('#define %s' % ifndef_name)
|
.Append('#define %s' % ifndef_name) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
(c.Append('#include <map>')
|
(c.Append('#include <map>') \
|
||||||
.Append('#include <string>')
|
.Append('#include <string>') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Concat(cpp_util.OpenNamespace(self._namespace))
|
.Concat(cpp_util.OpenNamespace(self._namespace)) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
(c.Append('class %s {' % self._class_name)
|
(c.Append('class %s {' % self._class_name) \
|
||||||
.Append(' public:')
|
.Append(' public:') \
|
||||||
.Sblock()
|
.Sblock() \
|
||||||
.Concat(self._GeneratePublicBody())
|
.Concat(self._GeneratePublicBody()) \
|
||||||
.Eblock()
|
.Eblock() \
|
||||||
.Append(' private:')
|
.Append(' private:') \
|
||||||
.Sblock()
|
.Sblock() \
|
||||||
.Concat(self._GeneratePrivateBody())
|
.Concat(self._GeneratePrivateBody()) \
|
||||||
.Eblock('};')
|
.Eblock('};') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Cblock(cpp_util.CloseNamespace(self._namespace))
|
.Cblock(cpp_util.CloseNamespace(self._namespace))
|
||||||
)
|
)
|
||||||
(c.Append('#endif // %s' % ifndef_name)
|
(c.Append('#endif // %s' % ifndef_name) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
@@ -71,14 +73,14 @@ class _Generator(object):
|
|||||||
def _GeneratePublicBody(self):
|
def _GeneratePublicBody(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
|
|
||||||
(c.Append('%s();' % self._class_name)
|
(c.Append('%s();' % self._class_name) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('enum ID {')
|
.Append('enum ID {') \
|
||||||
.Concat(self._GenerateEnumConstants())
|
.Concat(self._GenerateEnumConstants()) \
|
||||||
.Eblock('};')
|
.Eblock('};') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('const char* ToString(ID id) const;')
|
.Append('const char* ToString(ID id) const;') \
|
||||||
.Append('ID FromString(const std::string& id) const;')
|
.Append('ID FromString(const std::string& id) const;') \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
@@ -90,7 +92,7 @@ class _Generator(object):
|
|||||||
def _GenerateEnumConstants(self):
|
def _GenerateEnumConstants(self):
|
||||||
c = Code()
|
c = Code()
|
||||||
|
|
||||||
(c.Sblock()
|
(c.Sblock() \
|
||||||
.Append('kUnknown,')
|
.Append('kUnknown,')
|
||||||
)
|
)
|
||||||
for feature in self._feature_defs:
|
for feature in self._feature_defs:
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
# Copyright 2022 The Chromium Authors
|
# Copyright 2022 The Chromium Authors
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
"""Helper for quickly generating all known JS externs."""
|
"""Helper for quickly generating all known JS externs."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -31,6 +30,7 @@ REPO_ROOT = os.path.dirname(os.path.dirname(DIR))
|
|||||||
# Import the helper module.
|
# Import the helper module.
|
||||||
sys.path.insert(0, os.path.join(REPO_ROOT, 'extensions', 'common', 'api'))
|
sys.path.insert(0, os.path.join(REPO_ROOT, 'extensions', 'common', 'api'))
|
||||||
from externs_checker import ExternsChecker
|
from externs_checker import ExternsChecker
|
||||||
|
|
||||||
sys.path.pop(0)
|
sys.path.pop(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +72,7 @@ class FakeOutputApi:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class PresubmitResult:
|
class PresubmitResult:
|
||||||
|
|
||||||
def __init__(self, msg, long_text=None):
|
def __init__(self, msg, long_text=None):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.long_text = long_text
|
self.long_text = long_text
|
||||||
@@ -112,7 +113,7 @@ def Generate(input_api, output_api, force=False, dryrun=False):
|
|||||||
externs_relpath = input_api.os_path.relpath(externs, src_root)
|
externs_relpath = input_api.os_path.relpath(externs, src_root)
|
||||||
|
|
||||||
print('\r' + ' ' * msg_len, end='\r')
|
print('\r' + ' ' * msg_len, end='\r')
|
||||||
msg = 'Checking %s ...' % (source_relpath,)
|
msg = 'Checking %s ...' % (source_relpath, )
|
||||||
msg_len = len(msg)
|
msg_len = len(msg)
|
||||||
print(msg, end='')
|
print(msg, end='')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@@ -123,9 +124,9 @@ def Generate(input_api, output_api, force=False, dryrun=False):
|
|||||||
if not dryrun:
|
if not dryrun:
|
||||||
print('\n%s: %s' % (source_relpath, e))
|
print('\n%s: %s' % (source_relpath, e))
|
||||||
ret.append(
|
ret.append(
|
||||||
output_api.PresubmitResult(
|
output_api.PresubmitResult('%s: unable to generate' %
|
||||||
'%s: unable to generate' % (source_relpath,),
|
(source_relpath, ),
|
||||||
long_text=str(e)))
|
long_text=str(e)))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Ignore the first line (copyright) to avoid yearly thrashing.
|
# Ignore the first line (copyright) to avoid yearly thrashing.
|
||||||
@@ -148,7 +149,7 @@ def Generate(input_api, output_api, force=False, dryrun=False):
|
|||||||
if not dryrun:
|
if not dryrun:
|
||||||
print('\r' + ' ' * msg_len, end='\r')
|
print('\r' + ' ' * msg_len, end='\r')
|
||||||
msg_len = 0
|
msg_len = 0
|
||||||
print('Updating %s' % (externs_relpath,))
|
print('Updating %s' % (externs_relpath, ))
|
||||||
with open(externs, 'w', encoding='utf-8') as fp:
|
with open(externs, 'w', encoding='utf-8') as fp:
|
||||||
fp.write(copyright + '\n')
|
fp.write(copyright + '\n')
|
||||||
fp.write(new_data)
|
fp.write(new_data)
|
||||||
@@ -161,11 +162,16 @@ def Generate(input_api, output_api, force=False, dryrun=False):
|
|||||||
def get_parser():
|
def get_parser():
|
||||||
"""Get CLI parser."""
|
"""Get CLI parser."""
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument('-n', '--dry-run', dest='dryrun', action='store_true',
|
parser.add_argument('-n',
|
||||||
|
'--dry-run',
|
||||||
|
dest='dryrun',
|
||||||
|
action='store_true',
|
||||||
help="Don't make changes; only show changed files")
|
help="Don't make changes; only show changed files")
|
||||||
parser.add_argument('-f', '--force', action='store_true',
|
parser.add_argument('-f',
|
||||||
|
'--force',
|
||||||
|
action='store_true',
|
||||||
help='Regenerate files even if they have a TODO '
|
help='Regenerate files even if they have a TODO '
|
||||||
'disabling generation')
|
'disabling generation')
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@@ -174,7 +180,9 @@ def main(argv):
|
|||||||
parser = get_parser()
|
parser = get_parser()
|
||||||
opts = parser.parse_args(argv)
|
opts = parser.parse_args(argv)
|
||||||
|
|
||||||
results = Generate(FakeInputApi(), FakeOutputApi(), force=opts.force,
|
results = Generate(FakeInputApi(),
|
||||||
|
FakeOutputApi(),
|
||||||
|
force=opts.force,
|
||||||
dryrun=opts.dryrun)
|
dryrun=opts.dryrun)
|
||||||
if opts.dryrun and results:
|
if opts.dryrun and results:
|
||||||
for result in results:
|
for result in results:
|
||||||
|
@@ -9,7 +9,9 @@ from model import PropertyType, Type, Property
|
|||||||
import cpp_util
|
import cpp_util
|
||||||
import schema_util
|
import schema_util
|
||||||
|
|
||||||
|
|
||||||
class HGenerator(object):
|
class HGenerator(object):
|
||||||
|
|
||||||
def __init__(self, type_generator):
|
def __init__(self, type_generator):
|
||||||
self._type_generator = type_generator
|
self._type_generator = type_generator
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@ class HGenerator(object):
|
|||||||
class _Generator(object):
|
class _Generator(object):
|
||||||
"""A .h generator for a namespace.
|
"""A .h generator for a namespace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, namespace, cpp_type_generator):
|
def __init__(self, namespace, cpp_type_generator):
|
||||||
self._namespace = namespace
|
self._namespace = namespace
|
||||||
self._type_helper = cpp_type_generator
|
self._type_helper = cpp_type_generator
|
||||||
@@ -30,10 +33,10 @@ class _Generator(object):
|
|||||||
"""Generates a Code object with the .h for a single namespace.
|
"""Generates a Code object with the .h for a single namespace.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append(cpp_util.CHROMIUM_LICENSE)
|
(c.Append(cpp_util.CHROMIUM_LICENSE) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append(cpp_util.GENERATED_FILE_MESSAGE %
|
.Append(cpp_util.GENERATED_FILE_MESSAGE %
|
||||||
cpp_util.ToPosixPath(self._namespace.source_file))
|
cpp_util.ToPosixPath(self._namespace.source_file)) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,21 +51,21 @@ class _Generator(object):
|
|||||||
# non-optional types from other namespaces.
|
# non-optional types from other namespaces.
|
||||||
include_soft = self._namespace.name not in ('tabs', 'windows')
|
include_soft = self._namespace.name not in ('tabs', 'windows')
|
||||||
|
|
||||||
(c.Append('#ifndef %s' % ifndef_name)
|
(c.Append('#ifndef %s' % ifndef_name) \
|
||||||
.Append('#define %s' % ifndef_name)
|
.Append('#define %s' % ifndef_name) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include <stdint.h>')
|
.Append('#include <stdint.h>') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include <map>')
|
.Append('#include <map>') \
|
||||||
.Append('#include <memory>')
|
.Append('#include <memory>') \
|
||||||
.Append('#include <optional>')
|
.Append('#include <optional>') \
|
||||||
.Append('#include <string>')
|
.Append('#include <string>') \
|
||||||
.Append('#include <vector>')
|
.Append('#include <vector>') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#include "base/values.h"')
|
.Append('#include "base/values.h"') \
|
||||||
.Cblock(self._type_helper.GenerateIncludes(
|
.Cblock(self._type_helper.GenerateIncludes(
|
||||||
include_soft=include_soft,
|
include_soft=include_soft,
|
||||||
generate_error_messages=self._generate_error_messages))
|
generate_error_messages=self._generate_error_messages)) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,22 +80,21 @@ class _Generator(object):
|
|||||||
c.Concat(cpp_util.OpenNamespace(cpp_namespace))
|
c.Concat(cpp_util.OpenNamespace(cpp_namespace))
|
||||||
c.Append()
|
c.Append()
|
||||||
if self._namespace.properties:
|
if self._namespace.properties:
|
||||||
(c.Append('//')
|
(c.Append('//') \
|
||||||
.Append('// Properties')
|
.Append('// Properties') \
|
||||||
.Append('//')
|
.Append('//') \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
for prop in self._namespace.properties.values():
|
for prop in self._namespace.properties.values():
|
||||||
property_code = self._type_helper.GeneratePropertyValues(
|
property_code = self._type_helper.GeneratePropertyValues(
|
||||||
prop,
|
prop, 'extern const %(type)s %(name)s;')
|
||||||
'extern const %(type)s %(name)s;')
|
|
||||||
if property_code:
|
if property_code:
|
||||||
c.Cblock(property_code)
|
c.Cblock(property_code)
|
||||||
if self._namespace.types:
|
if self._namespace.types:
|
||||||
(c.Append('//')
|
(c.Append('//') \
|
||||||
.Append('// Types')
|
.Append('// Types') \
|
||||||
.Append('//')
|
.Append('//') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Cblock(self._GenerateTypes(self._FieldDependencyOrder(),
|
.Cblock(self._GenerateTypes(self._FieldDependencyOrder(),
|
||||||
is_toplevel=True,
|
is_toplevel=True,
|
||||||
generate_typedefs=True))
|
generate_typedefs=True))
|
||||||
@@ -104,24 +106,24 @@ class _Generator(object):
|
|||||||
c.Append()
|
c.Append()
|
||||||
c.Cblock(self._GenerateManifestKeys())
|
c.Cblock(self._GenerateManifestKeys())
|
||||||
if self._namespace.functions:
|
if self._namespace.functions:
|
||||||
(c.Append('//')
|
(c.Append('//') \
|
||||||
.Append('// Functions')
|
.Append('// Functions') \
|
||||||
.Append('//')
|
.Append('//') \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
for function in self._namespace.functions.values():
|
for function in self._namespace.functions.values():
|
||||||
c.Cblock(self._GenerateFunction(function))
|
c.Cblock(self._GenerateFunction(function))
|
||||||
if self._namespace.events:
|
if self._namespace.events:
|
||||||
(c.Append('//')
|
(c.Append('//') \
|
||||||
.Append('// Events')
|
.Append('// Events') \
|
||||||
.Append('//')
|
.Append('//') \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
for event in self._namespace.events.values():
|
for event in self._namespace.events.values():
|
||||||
c.Cblock(self._GenerateEvent(event))
|
c.Cblock(self._GenerateEvent(event))
|
||||||
(c.Concat(cpp_util.CloseNamespace(cpp_namespace))
|
(c.Concat(cpp_util.CloseNamespace(cpp_namespace)) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Append('#endif // %s' % ifndef_name)
|
.Append('#endif // %s' % ifndef_name) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
@@ -137,8 +139,8 @@ class _Generator(object):
|
|||||||
raise ValueError("Illegal circular dependency via cycle " +
|
raise ValueError("Illegal circular dependency via cycle " +
|
||||||
", ".join(map(lambda x: x.name, path + [type_])))
|
", ".join(map(lambda x: x.name, path + [type_])))
|
||||||
for prop in type_.properties.values():
|
for prop in type_.properties.values():
|
||||||
if (prop.type_ == PropertyType.REF and
|
if (prop.type_ == PropertyType.REF and schema_util.GetNamespace(
|
||||||
schema_util.GetNamespace(prop.ref_type) == self._namespace.name):
|
prop.ref_type) == self._namespace.name):
|
||||||
ExpandType(path + [type_], self._namespace.types[prop.ref_type])
|
ExpandType(path + [type_], self._namespace.types[prop.ref_type])
|
||||||
if not type_ in dependency_order:
|
if not type_ in dependency_order:
|
||||||
dependency_order.append(type_)
|
dependency_order.append(type_)
|
||||||
@@ -151,23 +153,23 @@ class _Generator(object):
|
|||||||
"""Generate a code object with the declaration of a C++ enum.
|
"""Generate a code object with the declaration of a C++ enum.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Sblock('enum class {name} {{'.format(
|
c.Sblock('enum class {name} {{'.format(name=enum_name))
|
||||||
name=enum_name))
|
|
||||||
|
|
||||||
# Explicitly initialize kNone to 0, since we rely on default initialization
|
# Explicitly initialize kNone to 0, since we rely on default initialization
|
||||||
# for enum members. Otherwise, default initialization will always set a
|
# for enum members. Otherwise, default initialization will always set a
|
||||||
# value to 0, even if it's not a valid enum entry.
|
# value to 0, even if it's not a valid enum entry.
|
||||||
c.Append(
|
c.Append(
|
||||||
self._type_helper.GetEnumNoneValue(type_, full_name=False) + ' = 0,')
|
self._type_helper.GetEnumNoneValue(type_, full_name=False) + ' = 0,')
|
||||||
|
|
||||||
for value in type_.enum_values:
|
for value in type_.enum_values:
|
||||||
current_enum_string = (
|
current_enum_string = (self._type_helper.GetEnumValue(type_,
|
||||||
self._type_helper.GetEnumValue(type_, value, full_name=False))
|
value,
|
||||||
|
full_name=False))
|
||||||
c.Append(current_enum_string + ',')
|
c.Append(current_enum_string + ',')
|
||||||
|
|
||||||
# Adding kMaxValue, which is friendly to enumaration histogram macros.
|
# Adding kMaxValue, which is friendly to enumaration histogram macros.
|
||||||
c.Append('kMaxValue = {last_key_value},'.format(
|
c.Append('kMaxValue = {last_key_value},'.format(
|
||||||
last_key_value=current_enum_string))
|
last_key_value=current_enum_string))
|
||||||
|
|
||||||
c.Eblock('};')
|
c.Eblock('};')
|
||||||
return c
|
return c
|
||||||
@@ -183,10 +185,8 @@ class _Generator(object):
|
|||||||
needs_blank_line = True
|
needs_blank_line = True
|
||||||
if prop.description:
|
if prop.description:
|
||||||
c.Comment(prop.description)
|
c.Comment(prop.description)
|
||||||
(c.Append('%s %s;' % (
|
(c.Append('%s %s;' % (self._type_helper.GetCppType(
|
||||||
self._type_helper.GetCppType(prop.type_, is_optional=prop.optional),
|
prop.type_, is_optional=prop.optional), prop.unix_name)))
|
||||||
prop.unix_name))
|
|
||||||
)
|
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False):
|
def _GenerateType(self, type_, is_toplevel=False, generate_typedefs=False):
|
||||||
@@ -204,7 +204,7 @@ class _Generator(object):
|
|||||||
|
|
||||||
if type_.functions:
|
if type_.functions:
|
||||||
# Wrap functions within types in the type's namespace.
|
# Wrap functions within types in the type's namespace.
|
||||||
(c.Append('namespace %s {' % classname)
|
(c.Append('namespace %s {' % classname) \
|
||||||
.Append()
|
.Append()
|
||||||
)
|
)
|
||||||
for function in type_.functions.values():
|
for function in type_.functions.values():
|
||||||
@@ -217,10 +217,8 @@ class _Generator(object):
|
|||||||
if generate_typedefs:
|
if generate_typedefs:
|
||||||
item_cpp_type = self._type_helper.GetCppType(type_.item_type)
|
item_cpp_type = self._type_helper.GetCppType(type_.item_type)
|
||||||
if item_cpp_type != 'base::Value':
|
if item_cpp_type != 'base::Value':
|
||||||
(c.Append('using %s = std::vector<%s >;' % (
|
(c.Append('using %s = std::vector<%s >;' %
|
||||||
classname,
|
(classname, item_cpp_type)))
|
||||||
item_cpp_type))
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
c.Append('using %s = base::Value::List;' % classname)
|
c.Append('using %s = base::Value::List;' % classname)
|
||||||
elif type_.property_type == PropertyType.STRING:
|
elif type_.property_type == PropertyType.STRING:
|
||||||
@@ -235,26 +233,25 @@ class _Generator(object):
|
|||||||
# Top level enums are in a namespace scope so the methods shouldn't be
|
# Top level enums are in a namespace scope so the methods shouldn't be
|
||||||
# static. On the other hand, those declared inline (e.g. in an object) do.
|
# static. On the other hand, those declared inline (e.g. in an object) do.
|
||||||
maybe_static = '' if is_toplevel else 'static '
|
maybe_static = '' if is_toplevel else 'static '
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Append('%sconst char* ToString(%s as_enum);' %
|
.Append('%sconst char* ToString(%s as_enum);' %
|
||||||
(maybe_static, classname))
|
(maybe_static, classname)) \
|
||||||
.Append('%s%s Parse%s(std::string_view as_string);' %
|
.Append('%s%s Parse%s(std::string_view as_string);' %
|
||||||
(maybe_static, classname, classname))
|
(maybe_static, classname, classname)) \
|
||||||
.Append(
|
.Append(
|
||||||
'%sstd::u16string Get%sParseError(std::string_view as_string);' %
|
'%sstd::u16string Get%sParseError(std::string_view as_string);' %
|
||||||
(maybe_static, classname))
|
(maybe_static, classname))
|
||||||
)
|
)
|
||||||
elif type_.property_type in (PropertyType.CHOICES,
|
elif type_.property_type in (PropertyType.CHOICES, PropertyType.OBJECT):
|
||||||
PropertyType.OBJECT):
|
|
||||||
if type_.description:
|
if type_.description:
|
||||||
c.Comment(type_.description)
|
c.Comment(type_.description)
|
||||||
|
|
||||||
(c.Sblock('struct %(classname)s {')
|
(c.Sblock('struct %(classname)s {') \
|
||||||
.Append('%(classname)s();')
|
.Append('%(classname)s();') \
|
||||||
.Append('~%(classname)s();')
|
.Append('~%(classname)s();') \
|
||||||
.Append('%(classname)s(const %(classname)s&) = delete;')
|
.Append('%(classname)s(const %(classname)s&) = delete;') \
|
||||||
.Append('%(classname)s& operator=(const %(classname)s&) = delete;')
|
.Append('%(classname)s& operator=(const %(classname)s&) = delete;') \
|
||||||
.Append('%(classname)s(%(classname)s&& rhs) noexcept;')
|
.Append('%(classname)s(%(classname)s&& rhs) noexcept;') \
|
||||||
.Append('%(classname)s& operator=(%(classname)s&& rhs) noexcept;')
|
.Append('%(classname)s& operator=(%(classname)s&& rhs) noexcept;')
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -263,28 +260,27 @@ class _Generator(object):
|
|||||||
c.Comment('Manifest key constants.')
|
c.Comment('Manifest key constants.')
|
||||||
c.Concat(self._GenerateManifestKeyConstants(type_.properties.values()))
|
c.Concat(self._GenerateManifestKeyConstants(type_.properties.values()))
|
||||||
|
|
||||||
value_type = ('base::Value'
|
value_type = ('base::Value' if type_.property_type is PropertyType.CHOICES
|
||||||
if type_.property_type is PropertyType.CHOICES else
|
else 'base::Value::Dict')
|
||||||
'base::Value::Dict')
|
|
||||||
|
|
||||||
if (type_.origin.from_json or
|
if (type_.origin.from_json
|
||||||
(type_.origin.from_manifest_keys and
|
or (type_.origin.from_manifest_keys
|
||||||
type_.property_type is PropertyType.CHOICES)):
|
and type_.property_type is PropertyType.CHOICES)):
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Populates a %s object from a base::Value& instance. Returns'
|
.Comment('Populates a %s object from a base::Value& instance. Returns'
|
||||||
' whether |out| was successfully populated.' % classname)
|
' whether |out| was successfully populated.' % classname) \
|
||||||
.Append('static bool Populate(%s);' % self._GenerateParams(
|
.Append('static bool Populate(%s);' % self._GenerateParams(
|
||||||
('const base::Value& value', '%s& out' % classname)))
|
('const base::Value& value', '%s& out' % classname)))
|
||||||
)
|
)
|
||||||
if type_.property_type is not PropertyType.CHOICES:
|
if type_.property_type is not PropertyType.CHOICES:
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Populates a %s object from a Dict& instance. Returns'
|
.Comment('Populates a %s object from a Dict& instance. Returns'
|
||||||
' whether |out| was successfully populated.' % classname)
|
' whether |out| was successfully populated.' % classname) \
|
||||||
.Append('static bool Populate(%s);' % self._GenerateParams(
|
.Append('static bool Populate(%s);' % self._GenerateParams(
|
||||||
('const base::Value::Dict& value', '%s& out' % classname)))
|
('const base::Value::Dict& value', '%s& out' % classname)))
|
||||||
)
|
)
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Creates a deep copy of %s.' % classname)
|
.Comment('Creates a deep copy of %s.' % classname) \
|
||||||
.Append('%s Clone() const;' % classname)
|
.Append('%s Clone() const;' % classname)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -292,32 +288,32 @@ class _Generator(object):
|
|||||||
classname, support_errors=self._generate_error_messages)
|
classname, support_errors=self._generate_error_messages)
|
||||||
|
|
||||||
if type_.property_type is not PropertyType.CHOICES:
|
if type_.property_type is not PropertyType.CHOICES:
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Creates a {classname} object from a base::Value::Dict,'
|
.Comment('Creates a {classname} object from a base::Value::Dict,'
|
||||||
' or {failure} on failure.'.format(
|
' or {failure} on failure.'.format(
|
||||||
classname=classname,
|
classname=classname,
|
||||||
failure=('unexpected'
|
failure=('unexpected'
|
||||||
if self._generate_error_messages else 'nullopt')))
|
if self._generate_error_messages else 'nullopt'))) \
|
||||||
.Append('static {return_type} '
|
.Append('static {return_type} '
|
||||||
'FromValue(const base::Value::Dict& value);'.format(
|
'FromValue(const base::Value::Dict& value);'.format(
|
||||||
return_type=return_type))
|
return_type=return_type))
|
||||||
)
|
)
|
||||||
|
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Creates a {classname} object from a base::Value,'
|
.Comment('Creates a {classname} object from a base::Value,'
|
||||||
' or {failure} on failure.'.format(
|
' or {failure} on failure.'.format(
|
||||||
classname=classname,
|
classname=classname,
|
||||||
failure=('unexpected'
|
failure=('unexpected'
|
||||||
if self._generate_error_messages else 'nullopt')))
|
if self._generate_error_messages else 'nullopt'))) \
|
||||||
.Append('static {return_type} '
|
.Append('static {return_type} '
|
||||||
'FromValue(const base::Value& value);'.format(
|
'FromValue(const base::Value& value);'.format(
|
||||||
return_type=return_type))
|
return_type=return_type))
|
||||||
)
|
)
|
||||||
|
|
||||||
if type_.origin.from_client:
|
if type_.origin.from_client:
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Comment('Returns a new %s representing the serialized form of this'
|
.Comment('Returns a new %s representing the serialized form of this'
|
||||||
'%s object.' % (value_type, classname))
|
'%s object.' % (value_type, classname)) \
|
||||||
.Append('%s ToValue() const;' % value_type)
|
.Append('%s ToValue() const;' % value_type)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -330,13 +326,12 @@ class _Generator(object):
|
|||||||
c.Cblock(self._GenerateTypes(type_.choices))
|
c.Cblock(self._GenerateTypes(type_.choices))
|
||||||
c.Append('// Choices:')
|
c.Append('// Choices:')
|
||||||
for choice_type in type_.choices:
|
for choice_type in type_.choices:
|
||||||
c.Append('%s as_%s;' % (
|
c.Append('%s as_%s;' % (self._type_helper.GetCppType(
|
||||||
self._type_helper.GetCppType(choice_type, is_optional=True),
|
choice_type, is_optional=True), choice_type.unix_name))
|
||||||
choice_type.unix_name))
|
|
||||||
else:
|
else:
|
||||||
properties = type_.properties.values()
|
properties = type_.properties.values()
|
||||||
(c.Append()
|
(c.Append() \
|
||||||
.Cblock(self._GenerateTypes(p.type_ for p in properties))
|
.Cblock(self._GenerateTypes(p.type_ for p in properties)) \
|
||||||
.Cblock(self._GenerateFields(properties)))
|
.Cblock(self._GenerateFields(properties)))
|
||||||
if type_.additional_properties is not None:
|
if type_.additional_properties is not None:
|
||||||
# Most additionalProperties actually have type "any", which is better
|
# Most additionalProperties actually have type "any", which is better
|
||||||
@@ -344,7 +339,7 @@ class _Generator(object):
|
|||||||
if type_.additional_properties.property_type == PropertyType.ANY:
|
if type_.additional_properties.property_type == PropertyType.ANY:
|
||||||
c.Append('base::Value::Dict additional_properties;')
|
c.Append('base::Value::Dict additional_properties;')
|
||||||
else:
|
else:
|
||||||
(c.Cblock(self._GenerateType(type_.additional_properties))
|
(c.Cblock(self._GenerateType(type_.additional_properties)) \
|
||||||
.Append('std::map<std::string, %s> additional_properties;' %
|
.Append('std::map<std::string, %s> additional_properties;' %
|
||||||
self._type_helper.GetCppType(type_.additional_properties))
|
self._type_helper.GetCppType(type_.additional_properties))
|
||||||
)
|
)
|
||||||
@@ -357,10 +352,10 @@ class _Generator(object):
|
|||||||
c = Code()
|
c = Code()
|
||||||
# TODO(kalman): use event.unix_name not Classname.
|
# TODO(kalman): use event.unix_name not Classname.
|
||||||
event_namespace = cpp_util.Classname(event.name)
|
event_namespace = cpp_util.Classname(event.name)
|
||||||
(c.Append('namespace %s {' % event_namespace)
|
(c.Append('namespace %s {' % event_namespace) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Concat(self._GenerateEventNameConstant(event))
|
.Concat(self._GenerateEventNameConstant(event)) \
|
||||||
.Concat(self._GenerateAsyncResponseArguments(event.params))
|
.Concat(self._GenerateAsyncResponseArguments(event.params)) \
|
||||||
.Append('} // namespace %s' % event_namespace)
|
.Append('} // namespace %s' % event_namespace)
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
@@ -375,8 +370,8 @@ class _Generator(object):
|
|||||||
# to not use the name.
|
# to not use the name.
|
||||||
if function_namespace == 'SendMessage':
|
if function_namespace == 'SendMessage':
|
||||||
function_namespace = 'PassMessage'
|
function_namespace = 'PassMessage'
|
||||||
(c.Append('namespace %s {' % function_namespace)
|
(c.Append('namespace %s {' % function_namespace) \
|
||||||
.Append()
|
.Append() \
|
||||||
.Cblock(self._GenerateFunctionParams(function))
|
.Cblock(self._GenerateFunctionParams(function))
|
||||||
)
|
)
|
||||||
if function.returns_async:
|
if function.returns_async:
|
||||||
@@ -394,26 +389,26 @@ class _Generator(object):
|
|||||||
(c.Sblock('struct Params {'))
|
(c.Sblock('struct Params {'))
|
||||||
if self._generate_error_messages:
|
if self._generate_error_messages:
|
||||||
(c.Append('static base::expected<Params, std::u16string> '
|
(c.Append('static base::expected<Params, std::u16string> '
|
||||||
'Create(const base::Value::List& args);')
|
'Create(const base::Value::List& args);') \
|
||||||
.Comment('DEPRECATED: prefer the variant of this function '
|
.Comment('DEPRECATED: prefer the variant of this function '
|
||||||
'returning errors with `base::expected`.')
|
'returning errors with `base::expected`.')
|
||||||
)
|
)
|
||||||
|
|
||||||
(c.Append('static std::optional<Params> Create(%s);' %
|
(c.Append('static std::optional<Params> Create(%s);' %
|
||||||
self._GenerateParams(
|
self._GenerateParams(
|
||||||
('const base::Value::List& args',)))
|
('const base::Value::List& args',))) \
|
||||||
.Append('Params(const Params&) = delete;')
|
.Append('Params(const Params&) = delete;') \
|
||||||
.Append('Params& operator=(const Params&) = delete;')
|
.Append('Params& operator=(const Params&) = delete;') \
|
||||||
.Append('Params(Params&& rhs) noexcept;')
|
.Append('Params(Params&& rhs) noexcept;') \
|
||||||
.Append('Params& operator=(Params&& rhs) noexcept;')
|
.Append('Params& operator=(Params&& rhs) noexcept;') \
|
||||||
.Append('~Params();')
|
.Append('~Params();') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Cblock(self._GenerateTypes(p.type_ for p in function.params))
|
.Cblock(self._GenerateTypes(p.type_ for p in function.params)) \
|
||||||
.Cblock(self._GenerateFields(function.params))
|
.Cblock(self._GenerateFields(function.params)) \
|
||||||
.Eblock()
|
.Eblock() \
|
||||||
.Append()
|
.Append() \
|
||||||
.Sblock(' private:')
|
.Sblock(' private:') \
|
||||||
.Append('Params();')
|
.Append('Params();') \
|
||||||
.Eblock('};')
|
.Eblock('};')
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
@@ -424,9 +419,10 @@ class _Generator(object):
|
|||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
for type_ in types:
|
for type_ in types:
|
||||||
c.Cblock(self._GenerateType(type_,
|
c.Cblock(
|
||||||
is_toplevel=is_toplevel,
|
self._GenerateType(type_,
|
||||||
generate_typedefs=generate_typedefs))
|
is_toplevel=is_toplevel,
|
||||||
|
generate_typedefs=generate_typedefs))
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _GenerateManifestKeys(self):
|
def _GenerateManifestKeys(self):
|
||||||
@@ -446,26 +442,23 @@ class _Generator(object):
|
|||||||
# manifest types.
|
# manifest types.
|
||||||
if type_.IsRootManifestKeyType():
|
if type_.IsRootManifestKeyType():
|
||||||
params = [
|
params = [
|
||||||
'const base::Value::Dict& root_dict',
|
'const base::Value::Dict& root_dict',
|
||||||
'%s& out' % classname,
|
'%s& out' % classname, 'std::u16string& error'
|
||||||
'std::u16string& error'
|
|
||||||
]
|
]
|
||||||
comment = (
|
comment = (
|
||||||
'Parses manifest keys for this namespace. Any keys not available to the'
|
'Parses manifest keys for this namespace. Any keys not available to'
|
||||||
' manifest will be ignored. On a parsing error, false is returned and '
|
' the manifest will be ignored. On a parsing error, false is returned'
|
||||||
'|error| is populated.')
|
' and |error| is populated.')
|
||||||
else:
|
else:
|
||||||
params = [
|
params = [
|
||||||
'const base::Value::Dict& root_dict',
|
'const base::Value::Dict& root_dict', 'std::string_view key',
|
||||||
'std::string_view key',
|
'%s& out' % classname, 'std::u16string& error',
|
||||||
'%s& out' % classname,
|
'std::vector<std::string_view>& error_path_reversed'
|
||||||
'std::u16string& error',
|
|
||||||
'std::vector<std::string_view>& error_path_reversed'
|
|
||||||
]
|
]
|
||||||
comment = (
|
comment = (
|
||||||
'Parses the given |key| from |root_dict|. Any keys not available to the'
|
'Parses the given |key| from |root_dict|. Any keys not available to'
|
||||||
' manifest will be ignored. On a parsing error, false is returned and '
|
' the manifest will be ignored. On a parsing error, false is returned'
|
||||||
'|error| and |error_path_reversed| are populated.')
|
' and |error| and |error_path_reversed| are populated.')
|
||||||
|
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Append().Comment(comment)
|
c.Append().Comment(comment)
|
||||||
@@ -499,18 +492,18 @@ class _Generator(object):
|
|||||||
for param in params:
|
for param in params:
|
||||||
if param.description:
|
if param.description:
|
||||||
c.Comment(param.description)
|
c.Comment(param.description)
|
||||||
declaration_list.append(cpp_util.GetParameterDeclaration(
|
declaration_list.append(
|
||||||
param, self._type_helper.GetCppType(param.type_)))
|
cpp_util.GetParameterDeclaration(
|
||||||
c.Append('base::Value::List Create(%s);' %
|
param, self._type_helper.GetCppType(param.type_)))
|
||||||
', '.join(declaration_list))
|
c.Append('base::Value::List Create(%s);' % ', '.join(declaration_list))
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _GenerateEventNameConstant(self, event):
|
def _GenerateEventNameConstant(self, event):
|
||||||
"""Generates a constant string array for the event name.
|
"""Generates a constant string array for the event name.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Append('extern const char kEventName[]; // "%s.%s"' % (
|
c.Append('extern const char kEventName[]; // "%s.%s"' %
|
||||||
self._namespace.name, event.name))
|
(self._namespace.name, event.name))
|
||||||
c.Append()
|
c.Append()
|
||||||
return c
|
return c
|
||||||
|
|
||||||
@@ -518,15 +511,14 @@ class _Generator(object):
|
|||||||
"""Generates namespace for passing a function's result back.
|
"""Generates namespace for passing a function's result back.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append('namespace Results {')
|
(c.Append('namespace Results {') \
|
||||||
.Append()
|
.Append() \
|
||||||
.Concat(self._GenerateAsyncResponseArguments(returns_async.params))
|
.Concat(self._GenerateAsyncResponseArguments(returns_async.params)) \
|
||||||
.Append('} // namespace Results')
|
.Append('} // namespace Results')
|
||||||
)
|
)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _GenerateParams(
|
def _GenerateParams(self, params, generate_error_messages=None):
|
||||||
self, params, generate_error_messages=None):
|
|
||||||
"""Builds the parameter list for a function, given an array of parameters.
|
"""Builds the parameter list for a function, given an array of parameters.
|
||||||
If |generate_error_messages| is specified, it overrides
|
If |generate_error_messages| is specified, it overrides
|
||||||
|self._generate_error_messages|.
|
|self._generate_error_messages|.
|
||||||
@@ -539,5 +531,5 @@ class _Generator(object):
|
|||||||
if generate_error_messages is None:
|
if generate_error_messages is None:
|
||||||
generate_error_messages = self._generate_error_messages
|
generate_error_messages = self._generate_error_messages
|
||||||
if generate_error_messages:
|
if generate_error_messages:
|
||||||
params += ('std::u16string& error',)
|
params += ('std::u16string& error', )
|
||||||
return ', '.join(str(p) for p in params)
|
return ', '.join(str(p) for p in params)
|
||||||
|
@@ -32,6 +32,7 @@ else:
|
|||||||
finally:
|
finally:
|
||||||
sys.path.pop(0)
|
sys.path.pop(0)
|
||||||
|
|
||||||
|
|
||||||
def ProcessComment(comment):
|
def ProcessComment(comment):
|
||||||
'''
|
'''
|
||||||
Convert a comment into a parent comment and a list of parameter comments.
|
Convert a comment into a parent comment and a list of parameter comments.
|
||||||
@@ -57,6 +58,7 @@ def ProcessComment(comment):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def add_paragraphs(content):
|
def add_paragraphs(content):
|
||||||
paragraphs = content.split('\n\n')
|
paragraphs = content.split('\n\n')
|
||||||
if len(paragraphs) < 2:
|
if len(paragraphs) < 2:
|
||||||
@@ -69,8 +71,8 @@ def ProcessComment(comment):
|
|||||||
# Get the parent comment (everything before the first parameter comment.
|
# Get the parent comment (everything before the first parameter comment.
|
||||||
first_parameter_location = (parameter_starts[0].start()
|
first_parameter_location = (parameter_starts[0].start()
|
||||||
if parameter_starts else len(comment))
|
if parameter_starts else len(comment))
|
||||||
parent_comment = (add_paragraphs(comment[:first_parameter_location].strip())
|
parent_comment = (add_paragraphs(
|
||||||
.replace('\n', ''))
|
comment[:first_parameter_location].strip()).replace('\n', ''))
|
||||||
|
|
||||||
params = OrderedDict()
|
params = OrderedDict()
|
||||||
for (cur_param, next_param) in itertools.zip_longest(parameter_starts,
|
for (cur_param, next_param) in itertools.zip_longest(parameter_starts,
|
||||||
@@ -81,9 +83,9 @@ def ProcessComment(comment):
|
|||||||
# beginning of the next parameter's introduction.
|
# beginning of the next parameter's introduction.
|
||||||
param_comment_start = cur_param.end()
|
param_comment_start = cur_param.end()
|
||||||
param_comment_end = next_param.start() if next_param else len(comment)
|
param_comment_end = next_param.start() if next_param else len(comment)
|
||||||
params[param_name] = (
|
params[param_name] = (add_paragraphs(
|
||||||
add_paragraphs(comment[param_comment_start:param_comment_end].strip())
|
comment[param_comment_start:param_comment_end].strip()).replace(
|
||||||
.replace('\n', ''))
|
'\n', ''))
|
||||||
|
|
||||||
return (parent_comment, params)
|
return (parent_comment, params)
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ class Callspec(object):
|
|||||||
a tuple:
|
a tuple:
|
||||||
(name, list of function parameters, return type, async return)
|
(name, list of function parameters, return type, async return)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, callspec_node, comment):
|
def __init__(self, callspec_node, comment):
|
||||||
self.node = callspec_node
|
self.node = callspec_node
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
@@ -103,9 +106,10 @@ class Callspec(object):
|
|||||||
return_type = None
|
return_type = None
|
||||||
returns_async = None
|
returns_async = None
|
||||||
if self.node.GetProperty('TYPEREF') not in ('void', None):
|
if self.node.GetProperty('TYPEREF') not in ('void', None):
|
||||||
return_type = Typeref(self.node.GetProperty('TYPEREF'),
|
return_type = Typeref(self.node.GetProperty('TYPEREF'), self.node.parent,
|
||||||
self.node.parent,
|
{
|
||||||
{'name': self.node.GetName()}).process(callbacks)
|
'name': self.node.GetName()
|
||||||
|
}).process(callbacks)
|
||||||
# The IDL parser doesn't allow specifying return types as optional.
|
# The IDL parser doesn't allow specifying return types as optional.
|
||||||
# Instead we infer any object return values to be optional.
|
# Instead we infer any object return values to be optional.
|
||||||
# TODO(asargent): fix the IDL parser to support optional return types.
|
# TODO(asargent): fix the IDL parser to support optional return types.
|
||||||
@@ -127,32 +131,25 @@ class Callspec(object):
|
|||||||
# trailingCallbackIsFunctionParameter extended attribute).
|
# trailingCallbackIsFunctionParameter extended attribute).
|
||||||
# TODO(tjudkins): Once IDL definitions are changed to describe returning
|
# TODO(tjudkins): Once IDL definitions are changed to describe returning
|
||||||
# promises, we can condition on that instead.
|
# promises, we can condition on that instead.
|
||||||
if (
|
if (use_returns_async
|
||||||
use_returns_async
|
|
||||||
and not self.node.GetProperty('trailingCallbackIsFunctionParameter')
|
and not self.node.GetProperty('trailingCallbackIsFunctionParameter')
|
||||||
and len(parameters) > 0
|
and len(parameters) > 0 and parameters[-1].get('type') == 'function'):
|
||||||
and parameters[-1].get('type') == 'function'
|
|
||||||
):
|
|
||||||
returns_async = parameters.pop()
|
returns_async = parameters.pop()
|
||||||
# The returns_async field is inherently a function, so doesn't need type
|
# The returns_async field is inherently a function, so doesn't need type
|
||||||
# specified on it.
|
# specified on it.
|
||||||
returns_async.pop('type')
|
returns_async.pop('type')
|
||||||
does_not_support_promises = self.node.GetProperty(
|
does_not_support_promises = self.node.GetProperty(
|
||||||
'doesNotSupportPromises'
|
'doesNotSupportPromises')
|
||||||
)
|
|
||||||
if does_not_support_promises is not None:
|
if does_not_support_promises is not None:
|
||||||
returns_async['does_not_support_promises'] = does_not_support_promises
|
returns_async['does_not_support_promises'] = does_not_support_promises
|
||||||
else:
|
else:
|
||||||
assert return_type is None, (
|
assert return_type is None, (
|
||||||
'Function "%s" cannot support promises and also have a '
|
'Function "%s" cannot support promises and also have a '
|
||||||
'return value.' % self.node.GetName()
|
'return value.' % self.node.GetName())
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
assert not self.node.GetProperty('doesNotSupportPromises'), (
|
assert not self.node.GetProperty('doesNotSupportPromises'), (
|
||||||
'Callspec "%s" does not need to specify [doesNotSupportPromises] if '
|
'Callspec "%s" does not need to specify [doesNotSupportPromises] if '
|
||||||
'it does not have a trailing callback'
|
'it does not have a trailing callback' % self.node.GetName())
|
||||||
% self.node.GetName()
|
|
||||||
)
|
|
||||||
|
|
||||||
return (self.node.GetName(), parameters, return_type, returns_async)
|
return (self.node.GetName(), parameters, return_type, returns_async)
|
||||||
|
|
||||||
@@ -162,13 +159,14 @@ class Param(object):
|
|||||||
Given a Param node representing a function parameter, converts into a Python
|
Given a Param node representing a function parameter, converts into a Python
|
||||||
dictionary that the JSON schema compiler expects to see.
|
dictionary that the JSON schema compiler expects to see.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, param_node):
|
def __init__(self, param_node):
|
||||||
self.node = param_node
|
self.node = param_node
|
||||||
|
|
||||||
def process(self, callbacks):
|
def process(self, callbacks):
|
||||||
return Typeref(self.node.GetProperty('TYPEREF'),
|
return Typeref(self.node.GetProperty('TYPEREF'), self.node, {
|
||||||
self.node,
|
'name': self.node.GetName()
|
||||||
{'name': self.node.GetName()}).process(callbacks)
|
}).process(callbacks)
|
||||||
|
|
||||||
|
|
||||||
class Dictionary(object):
|
class Dictionary(object):
|
||||||
@@ -176,6 +174,7 @@ class Dictionary(object):
|
|||||||
Given an IDL Dictionary node, converts into a Python dictionary that the JSON
|
Given an IDL Dictionary node, converts into a Python dictionary that the JSON
|
||||||
schema compiler expects to see.
|
schema compiler expects to see.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, dictionary_node):
|
def __init__(self, dictionary_node):
|
||||||
self.node = dictionary_node
|
self.node = dictionary_node
|
||||||
|
|
||||||
@@ -185,9 +184,11 @@ class Dictionary(object):
|
|||||||
if node.cls == 'Member':
|
if node.cls == 'Member':
|
||||||
k, v = Member(node).process(callbacks)
|
k, v = Member(node).process(callbacks)
|
||||||
properties[k] = v
|
properties[k] = v
|
||||||
result = {'id': self.node.GetName(),
|
result = {
|
||||||
'properties': properties,
|
'id': self.node.GetName(),
|
||||||
'type': 'object'}
|
'properties': properties,
|
||||||
|
'type': 'object'
|
||||||
|
}
|
||||||
# If this has the `ignoreAdditionalProperties` extended attribute, copy it
|
# If this has the `ignoreAdditionalProperties` extended attribute, copy it
|
||||||
# into the resulting object with a value of True.
|
# into the resulting object with a value of True.
|
||||||
if self.node.GetProperty('ignoreAdditionalProperties'):
|
if self.node.GetProperty('ignoreAdditionalProperties'):
|
||||||
@@ -208,19 +209,22 @@ class Member(object):
|
|||||||
where the value is a Python dictionary that the JSON schema compiler expects
|
where the value is a Python dictionary that the JSON schema compiler expects
|
||||||
to see.
|
to see.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, member_node):
|
def __init__(self, member_node):
|
||||||
self.node = member_node
|
self.node = member_node
|
||||||
|
|
||||||
def process(
|
def process(self,
|
||||||
self, callbacks, functions_are_properties=False, use_returns_async=False
|
callbacks,
|
||||||
):
|
functions_are_properties=False,
|
||||||
|
use_returns_async=False):
|
||||||
properties = OrderedDict()
|
properties = OrderedDict()
|
||||||
name = self.node.GetName()
|
name = self.node.GetName()
|
||||||
if self.node.GetProperty('deprecated'):
|
if self.node.GetProperty('deprecated'):
|
||||||
properties['deprecated'] = self.node.GetProperty('deprecated')
|
properties['deprecated'] = self.node.GetProperty('deprecated')
|
||||||
|
|
||||||
for property_name in ['nodoc', 'nocompile', 'nodart',
|
for property_name in [
|
||||||
'serializableFunction']:
|
'nodoc', 'nocompile', 'nodart', 'serializableFunction'
|
||||||
|
]:
|
||||||
if self.node.GetProperty(property_name):
|
if self.node.GetProperty(property_name):
|
||||||
properties[property_name] = True
|
properties[property_name] = True
|
||||||
|
|
||||||
@@ -230,27 +234,24 @@ class Member(object):
|
|||||||
if self.node.GetProperty('platforms'):
|
if self.node.GetProperty('platforms'):
|
||||||
properties['platforms'] = list(self.node.GetProperty('platforms'))
|
properties['platforms'] = list(self.node.GetProperty('platforms'))
|
||||||
|
|
||||||
for option_name, sanitizer in [
|
for option_name, sanitizer in [('maxListeners', int),
|
||||||
('maxListeners', int),
|
('supportsFilters', lambda s: s == 'true'),
|
||||||
('supportsFilters', lambda s: s == 'true'),
|
('supportsListeners', lambda s: s == 'true'),
|
||||||
('supportsListeners', lambda s: s == 'true'),
|
('supportsRules', lambda s: s == 'true')]:
|
||||||
('supportsRules', lambda s: s == 'true')]:
|
|
||||||
if self.node.GetProperty(option_name):
|
if self.node.GetProperty(option_name):
|
||||||
if 'options' not in properties:
|
if 'options' not in properties:
|
||||||
properties['options'] = {}
|
properties['options'] = {}
|
||||||
properties['options'][option_name] = sanitizer(self.node.GetProperty(
|
properties['options'][option_name] = sanitizer(
|
||||||
option_name))
|
self.node.GetProperty(option_name))
|
||||||
type_override = None
|
type_override = None
|
||||||
parameter_comments = OrderedDict()
|
parameter_comments = OrderedDict()
|
||||||
for node in self.node.GetChildren():
|
for node in self.node.GetChildren():
|
||||||
if node.cls == 'Comment':
|
if node.cls == 'Comment':
|
||||||
(parent_comment, parameter_comments) = ProcessComment(
|
(parent_comment, parameter_comments) = ProcessComment(node.GetName())
|
||||||
node.GetName())
|
|
||||||
properties['description'] = parent_comment
|
properties['description'] = parent_comment
|
||||||
elif node.cls == 'Callspec':
|
elif node.cls == 'Callspec':
|
||||||
name, parameters, return_type, returns_async = Callspec(
|
name, parameters, return_type, returns_async = Callspec(
|
||||||
node, parameter_comments
|
node, parameter_comments).process(use_returns_async, callbacks)
|
||||||
).process(use_returns_async, callbacks)
|
|
||||||
if functions_are_properties:
|
if functions_are_properties:
|
||||||
# If functions are treated as properties (which will happen if the
|
# If functions are treated as properties (which will happen if the
|
||||||
# interface is named Properties) then this isn't a function, it's a
|
# interface is named Properties) then this isn't a function, it's a
|
||||||
@@ -258,14 +259,14 @@ class Member(object):
|
|||||||
# property type is the return type. This is an egregious hack in lieu
|
# property type is the return type. This is an egregious hack in lieu
|
||||||
# of the IDL parser supporting 'const'.
|
# of the IDL parser supporting 'const'.
|
||||||
assert parameters == [], (
|
assert parameters == [], (
|
||||||
'Property "%s" must be no-argument functions '
|
'Property "%s" must be no-argument functions '
|
||||||
'with a non-void return type' % name)
|
'with a non-void return type' % name)
|
||||||
assert return_type is not None, (
|
assert return_type is not None, (
|
||||||
'Property "%s" must be no-argument functions '
|
'Property "%s" must be no-argument functions '
|
||||||
'with a non-void return type' % name)
|
'with a non-void return type' % name)
|
||||||
assert 'type' in return_type, (
|
assert 'type' in return_type, (
|
||||||
'Property return type "%s" from "%s" must specify a '
|
'Property return type "%s" from "%s" must specify a '
|
||||||
'fundamental IDL type.' % (pprint.pformat(return_type), name))
|
'fundamental IDL type.' % (pprint.pformat(return_type), name))
|
||||||
type_override = return_type['type']
|
type_override = return_type['type']
|
||||||
else:
|
else:
|
||||||
type_override = 'function'
|
type_override = 'function'
|
||||||
@@ -279,8 +280,8 @@ class Member(object):
|
|||||||
if type_override is not None:
|
if type_override is not None:
|
||||||
properties['type'] = type_override
|
properties['type'] = type_override
|
||||||
else:
|
else:
|
||||||
properties = Typeref(self.node.GetProperty('TYPEREF'),
|
properties = Typeref(self.node.GetProperty('TYPEREF'), self.node,
|
||||||
self.node, properties).process(callbacks)
|
properties).process(callbacks)
|
||||||
value = self.node.GetProperty('value')
|
value = self.node.GetProperty('value')
|
||||||
if value is not None:
|
if value is not None:
|
||||||
# IDL always returns values as strings, so cast to their real type.
|
# IDL always returns values as strings, so cast to their real type.
|
||||||
@@ -298,9 +299,9 @@ class Member(object):
|
|||||||
return float(string_value)
|
return float(string_value)
|
||||||
# Add more as necessary.
|
# Add more as necessary.
|
||||||
assert json_type == 'string', (
|
assert json_type == 'string', (
|
||||||
'No rule exists to cast JSON Schema type "%s" to its equivalent '
|
'No rule exists to cast JSON Schema type "%s" to its equivalent '
|
||||||
'Python type for value "%s". You must add a new rule here.' %
|
'Python type for value "%s". You must add a new rule here.' %
|
||||||
(json_type, string_value))
|
(json_type, string_value))
|
||||||
return string_value
|
return string_value
|
||||||
|
|
||||||
|
|
||||||
@@ -310,6 +311,7 @@ class Typeref(object):
|
|||||||
function parameter, converts into a Python dictionary that the JSON schema
|
function parameter, converts into a Python dictionary that the JSON schema
|
||||||
compiler expects to see.
|
compiler expects to see.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, typeref, parent, additional_properties):
|
def __init__(self, typeref, parent, additional_properties):
|
||||||
self.typeref = typeref
|
self.typeref = typeref
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
@@ -363,11 +365,11 @@ class Typeref(object):
|
|||||||
properties['additionalProperties'] = OrderedDict()
|
properties['additionalProperties'] = OrderedDict()
|
||||||
properties['additionalProperties']['type'] = 'any'
|
properties['additionalProperties']['type'] = 'any'
|
||||||
elif self.parent.GetPropertyLocal('Union'):
|
elif self.parent.GetPropertyLocal('Union'):
|
||||||
properties['choices'] = [Typeref(node.GetProperty('TYPEREF'),
|
properties['choices'] = [
|
||||||
node,
|
Typeref(node.GetProperty('TYPEREF'), node,
|
||||||
OrderedDict()).process(callbacks)
|
OrderedDict()).process(callbacks)
|
||||||
for node in self.parent.GetChildren()
|
for node in self.parent.GetChildren() if node.cls == 'Option'
|
||||||
if node.cls == 'Option']
|
]
|
||||||
elif self.typeref is None:
|
elif self.typeref is None:
|
||||||
properties['type'] = 'function'
|
properties['type'] = 'function'
|
||||||
else:
|
else:
|
||||||
@@ -390,6 +392,7 @@ class Enum(object):
|
|||||||
Given an IDL Enum node, converts into a Python dictionary that the JSON
|
Given an IDL Enum node, converts into a Python dictionary that the JSON
|
||||||
schema compiler expects to see.
|
schema compiler expects to see.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, enum_node):
|
def __init__(self, enum_node):
|
||||||
self.node = enum_node
|
self.node = enum_node
|
||||||
self.description = ''
|
self.description = ''
|
||||||
@@ -411,12 +414,15 @@ class Enum(object):
|
|||||||
self.description = ProcessComment(node.GetName())[0]
|
self.description = ProcessComment(node.GetName())[0]
|
||||||
else:
|
else:
|
||||||
sys.exit('Did not process %s %s' % (node.cls, node))
|
sys.exit('Did not process %s %s' % (node.cls, node))
|
||||||
result = {'id' : self.node.GetName(),
|
result = {
|
||||||
'description': self.description,
|
'id': self.node.GetName(),
|
||||||
'type': 'string',
|
'description': self.description,
|
||||||
'enum': enum}
|
'type': 'string',
|
||||||
for property_name in ['cpp_enum_prefix_override', 'inline_doc',
|
'enum': enum
|
||||||
'noinline_doc', 'nodoc']:
|
}
|
||||||
|
for property_name in [
|
||||||
|
'cpp_enum_prefix_override', 'inline_doc', 'noinline_doc', 'nodoc'
|
||||||
|
]:
|
||||||
if self.node.GetProperty(property_name):
|
if self.node.GetProperty(property_name):
|
||||||
result[property_name] = self.node.GetProperty(property_name)
|
result[property_name] = self.node.GetProperty(property_name)
|
||||||
if self.node.GetProperty('deprecated'):
|
if self.node.GetProperty('deprecated'):
|
||||||
@@ -485,19 +491,19 @@ class Namespace(object):
|
|||||||
compiler_options = self.compiler_options or {}
|
compiler_options = self.compiler_options or {}
|
||||||
documentation_options = self.documentation_options or {}
|
documentation_options = self.documentation_options or {}
|
||||||
return {
|
return {
|
||||||
'namespace': self.namespace.GetName(),
|
'namespace': self.namespace.GetName(),
|
||||||
'description': self.description,
|
'description': self.description,
|
||||||
'nodoc': self.nodoc,
|
'nodoc': self.nodoc,
|
||||||
'types': self.types,
|
'types': self.types,
|
||||||
'functions': self.functions,
|
'functions': self.functions,
|
||||||
'properties': self.properties,
|
'properties': self.properties,
|
||||||
'manifest_keys': self.manifest_keys,
|
'manifest_keys': self.manifest_keys,
|
||||||
'internal': self.internal,
|
'internal': self.internal,
|
||||||
'events': self.events,
|
'events': self.events,
|
||||||
'platforms': self.platforms,
|
'platforms': self.platforms,
|
||||||
'compiler_options': compiler_options,
|
'compiler_options': compiler_options,
|
||||||
'deprecated': self.deprecated,
|
'deprecated': self.deprecated,
|
||||||
'documentation_options': documentation_options
|
'documentation_options': documentation_options
|
||||||
}
|
}
|
||||||
|
|
||||||
def process_interface(self, node, functions_are_properties=False):
|
def process_interface(self, node, functions_are_properties=False):
|
||||||
@@ -542,9 +548,12 @@ class IDLSchema(object):
|
|||||||
if not description:
|
if not description:
|
||||||
# TODO(kalman): Go back to throwing an error here.
|
# TODO(kalman): Go back to throwing an error here.
|
||||||
print('%s must have a namespace-level comment. This will '
|
print('%s must have a namespace-level comment. This will '
|
||||||
'appear on the API summary page.' % node.GetName())
|
'appear on the API summary page.' % node.GetName())
|
||||||
description = ''
|
description = ''
|
||||||
namespace = Namespace(node, description, nodoc, internal,
|
namespace = Namespace(node,
|
||||||
|
description,
|
||||||
|
nodoc,
|
||||||
|
internal,
|
||||||
platforms=platforms,
|
platforms=platforms,
|
||||||
compiler_options=compiler_options or None,
|
compiler_options=compiler_options or None,
|
||||||
deprecated=deprecated,
|
deprecated=deprecated,
|
||||||
@@ -621,8 +630,9 @@ def Main():
|
|||||||
contents = sys.stdin.read()
|
contents = sys.stdin.read()
|
||||||
for i, char in enumerate(contents):
|
for i, char in enumerate(contents):
|
||||||
if not char.isascii():
|
if not char.isascii():
|
||||||
raise Exception('Non-ascii character "%s" (ord %d) found at offset %d.'
|
raise Exception(
|
||||||
% (char, ord(char), i))
|
'Non-ascii character "%s" (ord %d) found at offset %d.' %
|
||||||
|
(char, ord(char), i))
|
||||||
idl = idl_parser.IDLParser().ParseData(contents, '<stdin>')
|
idl = idl_parser.IDLParser().ParseData(contents, '<stdin>')
|
||||||
schema = IDLSchema(idl).process()
|
schema = IDLSchema(idl).process()
|
||||||
print(json.dumps(schema, indent=2))
|
print(json.dumps(schema, indent=2))
|
||||||
|
@@ -8,6 +8,7 @@ import unittest
|
|||||||
|
|
||||||
from json_parse import OrderedDict
|
from json_parse import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
def getFunction(schema, name):
|
def getFunction(schema, name):
|
||||||
for item in schema['functions']:
|
for item in schema['functions']:
|
||||||
if item['name'] == name:
|
if item['name'] == name:
|
||||||
@@ -19,10 +20,12 @@ def getParams(schema, name):
|
|||||||
function = getFunction(schema, name)
|
function = getFunction(schema, name)
|
||||||
return function['parameters']
|
return function['parameters']
|
||||||
|
|
||||||
|
|
||||||
def getReturnsAsync(schema, name):
|
def getReturnsAsync(schema, name):
|
||||||
function = getFunction(schema, name)
|
function = getFunction(schema, name)
|
||||||
return function.get('returns_async', False)
|
return function.get('returns_async', False)
|
||||||
|
|
||||||
|
|
||||||
def getReturns(schema, name):
|
def getReturns(schema, name):
|
||||||
function = getFunction(schema, name)
|
function = getFunction(schema, name)
|
||||||
return function['returns']
|
return function['returns']
|
||||||
@@ -35,6 +38,7 @@ def getType(schema, id):
|
|||||||
|
|
||||||
|
|
||||||
class IdlSchemaTest(unittest.TestCase):
|
class IdlSchemaTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
loaded = idl_schema.Load('test/idl_basics.idl')
|
loaded = idl_schema.Load('test/idl_basics.idl')
|
||||||
self.assertEqual(1, len(loaded))
|
self.assertEqual(1, len(loaded))
|
||||||
@@ -44,66 +48,123 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testSimpleCallbacks(self):
|
def testSimpleCallbacks(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
expected = {'name': 'cb', 'parameters':[]}
|
expected = {'name': 'cb', 'parameters': []}
|
||||||
self.assertEqual(expected, getReturnsAsync(schema, 'function4'))
|
self.assertEqual(expected, getReturnsAsync(schema, 'function4'))
|
||||||
|
|
||||||
expected = {'name': 'cb',
|
expected = {'name': 'cb', 'parameters': [{'name': 'x', 'type': 'integer'}]}
|
||||||
'parameters':[{'name': 'x', 'type': 'integer'}]}
|
|
||||||
self.assertEqual(expected, getReturnsAsync(schema, 'function5'))
|
self.assertEqual(expected, getReturnsAsync(schema, 'function5'))
|
||||||
|
|
||||||
expected = {'name': 'cb',
|
expected = {
|
||||||
'parameters':[{'name': 'arg', '$ref': 'MyType1'}]}
|
'name': 'cb',
|
||||||
|
'parameters': [{
|
||||||
|
'name': 'arg',
|
||||||
|
'$ref': 'MyType1'
|
||||||
|
}]
|
||||||
|
}
|
||||||
self.assertEqual(expected, getReturnsAsync(schema, 'function6'))
|
self.assertEqual(expected, getReturnsAsync(schema, 'function6'))
|
||||||
|
|
||||||
def testCallbackWithArrayArgument(self):
|
def testCallbackWithArrayArgument(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
expected = {'name': 'cb',
|
expected = {
|
||||||
'parameters':[{'name': 'arg', 'type': 'array',
|
'name':
|
||||||
'items':{'$ref': 'MyType2'}}]}
|
'cb',
|
||||||
|
'parameters': [{
|
||||||
|
'name': 'arg',
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'$ref': 'MyType2'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
self.assertEqual(expected, getReturnsAsync(schema, 'function12'))
|
self.assertEqual(expected, getReturnsAsync(schema, 'function12'))
|
||||||
|
|
||||||
def testArrayOfCallbacks(self):
|
def testArrayOfCallbacks(self):
|
||||||
schema = idl_schema.Load('test/idl_function_types.idl')[0]
|
schema = idl_schema.Load('test/idl_function_types.idl')[0]
|
||||||
expected = [{'type': 'array', 'name': 'callbacks',
|
expected = [{
|
||||||
'items':{'type': 'function', 'name': 'MyCallback',
|
'type': 'array',
|
||||||
'parameters':[{'type': 'integer', 'name': 'x'}]}}]
|
'name': 'callbacks',
|
||||||
|
'items': {
|
||||||
|
'type': 'function',
|
||||||
|
'name': 'MyCallback',
|
||||||
|
'parameters': [{
|
||||||
|
'type': 'integer',
|
||||||
|
'name': 'x'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
self.assertEqual(expected, getParams(schema, 'whatever'))
|
self.assertEqual(expected, getParams(schema, 'whatever'))
|
||||||
|
|
||||||
def testProperties(self):
|
def testProperties(self):
|
||||||
self.assertEqual({
|
self.assertEqual(
|
||||||
'x': {'name': 'x', 'type': 'integer',
|
{
|
||||||
'description': 'This comment tests "double-quotes".'},
|
'x': {
|
||||||
'y': {'name': 'y', 'type': 'string'},
|
'name': 'x',
|
||||||
'z': {'name': 'z', 'type': 'string'},
|
'type': 'integer',
|
||||||
'a': {'name': 'a', 'type': 'string'},
|
'description': 'This comment tests "double-quotes".'
|
||||||
'b': {'name': 'b', 'type': 'string'},
|
},
|
||||||
'c': {'name': 'c', 'type': 'string'}},
|
'y': {
|
||||||
getType(self.idl_basics, 'MyType1')['properties'])
|
'name': 'y',
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'z': {
|
||||||
|
'name': 'z',
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'a': {
|
||||||
|
'name': 'a',
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'b': {
|
||||||
|
'name': 'b',
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'c': {
|
||||||
|
'name': 'c',
|
||||||
|
'type': 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getType(self.idl_basics, 'MyType1')['properties'])
|
||||||
|
|
||||||
def testMemberOrdering(self):
|
def testMemberOrdering(self):
|
||||||
self.assertEqual(
|
self.assertEqual(['x', 'y', 'z', 'a', 'b', 'c'],
|
||||||
['x', 'y', 'z', 'a', 'b', 'c'],
|
list(
|
||||||
list(getType(self.idl_basics, 'MyType1')['properties'].keys()))
|
getType(self.idl_basics,
|
||||||
|
'MyType1')['properties'].keys()))
|
||||||
|
|
||||||
def testEnum(self):
|
def testEnum(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
expected = {'enum': [{'name': 'name1', 'description': 'comment1'},
|
expected = {
|
||||||
{'name': 'name2'}],
|
'enum': [{
|
||||||
'description': 'Enum description',
|
'name': 'name1',
|
||||||
'type': 'string', 'id': 'EnumType'}
|
'description': 'comment1'
|
||||||
|
}, {
|
||||||
|
'name': 'name2'
|
||||||
|
}],
|
||||||
|
'description': 'Enum description',
|
||||||
|
'type': 'string',
|
||||||
|
'id': 'EnumType'
|
||||||
|
}
|
||||||
self.assertEqual(expected, getType(schema, expected['id']))
|
self.assertEqual(expected, getType(schema, expected['id']))
|
||||||
|
|
||||||
expected_params = [{'name': 'type', '$ref': 'EnumType'}]
|
expected_params = [{'name': 'type', '$ref': 'EnumType'}]
|
||||||
expected_returns_async = {
|
expected_returns_async = {
|
||||||
'name': 'cb',
|
'name': 'cb',
|
||||||
'parameters':[{'name': 'type', '$ref': 'EnumType'}]}
|
'parameters': [{
|
||||||
|
'name': 'type',
|
||||||
|
'$ref': 'EnumType'
|
||||||
|
}]
|
||||||
|
}
|
||||||
self.assertEqual(expected_params, getParams(schema, 'function13'))
|
self.assertEqual(expected_params, getParams(schema, 'function13'))
|
||||||
self.assertEqual(
|
self.assertEqual(expected_returns_async,
|
||||||
expected_returns_async, getReturnsAsync(schema, 'function13')
|
getReturnsAsync(schema, 'function13'))
|
||||||
)
|
|
||||||
|
|
||||||
expected = [{'items': {'$ref': 'EnumType'}, 'name': 'types',
|
expected = [{
|
||||||
'type': 'array'}]
|
'items': {
|
||||||
|
'$ref': 'EnumType'
|
||||||
|
},
|
||||||
|
'name': 'types',
|
||||||
|
'type': 'array'
|
||||||
|
}]
|
||||||
self.assertEqual(expected, getParams(schema, 'function14'))
|
self.assertEqual(expected, getParams(schema, 'function14'))
|
||||||
|
|
||||||
def testScopedArguments(self):
|
def testScopedArguments(self):
|
||||||
@@ -111,19 +172,29 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
expected = [{'name': 'value', '$ref': 'idl_other_namespace.SomeType'}]
|
expected = [{'name': 'value', '$ref': 'idl_other_namespace.SomeType'}]
|
||||||
self.assertEqual(expected, getParams(schema, 'function20'))
|
self.assertEqual(expected, getParams(schema, 'function20'))
|
||||||
|
|
||||||
expected = [{'items': {'$ref': 'idl_other_namespace.SomeType'},
|
expected = [{
|
||||||
'name': 'values',
|
'items': {
|
||||||
'type': 'array'}]
|
'$ref': 'idl_other_namespace.SomeType'
|
||||||
|
},
|
||||||
|
'name': 'values',
|
||||||
|
'type': 'array'
|
||||||
|
}]
|
||||||
self.assertEqual(expected, getParams(schema, 'function21'))
|
self.assertEqual(expected, getParams(schema, 'function21'))
|
||||||
|
|
||||||
expected = [{'name': 'value',
|
expected = [{
|
||||||
'$ref': 'idl_other_namespace.sub_namespace.AnotherType'}]
|
'name': 'value',
|
||||||
|
'$ref': 'idl_other_namespace.sub_namespace.AnotherType'
|
||||||
|
}]
|
||||||
self.assertEqual(expected, getParams(schema, 'function22'))
|
self.assertEqual(expected, getParams(schema, 'function22'))
|
||||||
|
|
||||||
expected = [{'items': {'$ref': 'idl_other_namespace.sub_namespace.'
|
expected = [{
|
||||||
'AnotherType'},
|
'items': {
|
||||||
'name': 'values',
|
'$ref': 'idl_other_namespace.sub_namespace.'
|
||||||
'type': 'array'}]
|
'AnotherType'
|
||||||
|
},
|
||||||
|
'name': 'values',
|
||||||
|
'type': 'array'
|
||||||
|
}]
|
||||||
self.assertEqual(expected, getParams(schema, 'function23'))
|
self.assertEqual(expected, getParams(schema, 'function23'))
|
||||||
|
|
||||||
def testNoCompile(self):
|
def testNoCompile(self):
|
||||||
@@ -151,45 +222,73 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
'name': 'name3',
|
'name': 'name3',
|
||||||
'description': 'comment3'
|
'description': 'comment3'
|
||||||
}],
|
}],
|
||||||
'type': 'string',
|
'type':
|
||||||
'id': 'EnumTypeWithNoDocValue',
|
'string',
|
||||||
'description': ''
|
'id':
|
||||||
|
'EnumTypeWithNoDocValue',
|
||||||
|
'description':
|
||||||
|
''
|
||||||
}
|
}
|
||||||
self.assertEqual(expected, getType(schema, expected['id']))
|
self.assertEqual(expected, getType(schema, expected['id']))
|
||||||
|
|
||||||
def testInternalNamespace(self):
|
def testInternalNamespace(self):
|
||||||
idl_basics = self.idl_basics
|
idl_basics = self.idl_basics
|
||||||
self.assertEqual('idl_basics', idl_basics['namespace'])
|
self.assertEqual('idl_basics', idl_basics['namespace'])
|
||||||
self.assertTrue(idl_basics['internal'])
|
self.assertTrue(idl_basics['internal'])
|
||||||
self.assertFalse(idl_basics['nodoc'])
|
self.assertFalse(idl_basics['nodoc'])
|
||||||
|
|
||||||
def testReturnTypes(self):
|
def testReturnTypes(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
self.assertEqual({'name': 'function24', 'type': 'integer'},
|
self.assertEqual({
|
||||||
getReturns(schema, 'function24'))
|
'name': 'function24',
|
||||||
self.assertEqual({'name': 'function25', '$ref': 'MyType1',
|
'type': 'integer'
|
||||||
'optional': True},
|
}, getReturns(schema, 'function24'))
|
||||||
getReturns(schema, 'function25'))
|
self.assertEqual({
|
||||||
self.assertEqual({'name': 'function26', 'type': 'array',
|
'name': 'function25',
|
||||||
'items': {'$ref': 'MyType1'}},
|
'$ref': 'MyType1',
|
||||||
getReturns(schema, 'function26'))
|
'optional': True
|
||||||
self.assertEqual({'name': 'function27', '$ref': 'EnumType',
|
}, getReturns(schema, 'function25'))
|
||||||
'optional': True},
|
self.assertEqual(
|
||||||
getReturns(schema, 'function27'))
|
{
|
||||||
self.assertEqual({'name': 'function28', 'type': 'array',
|
'name': 'function26',
|
||||||
'items': {'$ref': 'EnumType'}},
|
'type': 'array',
|
||||||
getReturns(schema, 'function28'))
|
'items': {
|
||||||
self.assertEqual({'name': 'function29', '$ref':
|
'$ref': 'MyType1'
|
||||||
'idl_other_namespace.SomeType',
|
}
|
||||||
'optional': True},
|
}, getReturns(schema, 'function26'))
|
||||||
getReturns(schema, 'function29'))
|
self.assertEqual(
|
||||||
self.assertEqual({'name': 'function30', 'type': 'array',
|
{
|
||||||
'items': {'$ref': 'idl_other_namespace.SomeType'}},
|
'name': 'function27',
|
||||||
getReturns(schema, 'function30'))
|
'$ref': 'EnumType',
|
||||||
|
'optional': True
|
||||||
|
}, getReturns(schema, 'function27'))
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
'name': 'function28',
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'$ref': 'EnumType'
|
||||||
|
}
|
||||||
|
}, getReturns(schema, 'function28'))
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
'name': 'function29',
|
||||||
|
'$ref': 'idl_other_namespace.SomeType',
|
||||||
|
'optional': True
|
||||||
|
}, getReturns(schema, 'function29'))
|
||||||
|
self.assertEqual(
|
||||||
|
{
|
||||||
|
'name': 'function30',
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'$ref': 'idl_other_namespace.SomeType'
|
||||||
|
}
|
||||||
|
}, getReturns(schema, 'function30'))
|
||||||
|
|
||||||
def testIgnoresAdditionalPropertiesOnType(self):
|
def testIgnoresAdditionalPropertiesOnType(self):
|
||||||
self.assertTrue(getType(self.idl_basics, 'IgnoreAdditionalPropertiesType')
|
self.assertTrue(
|
||||||
['ignoreAdditionalProperties'])
|
getType(self.idl_basics,
|
||||||
|
'IgnoreAdditionalPropertiesType')['ignoreAdditionalProperties'])
|
||||||
|
|
||||||
def testChromeOSPlatformsNamespace(self):
|
def testChromeOSPlatformsNamespace(self):
|
||||||
schema = idl_schema.Load('test/idl_namespace_chromeos.idl')[0]
|
schema = idl_schema.Load('test/idl_namespace_chromeos.idl')[0]
|
||||||
@@ -206,7 +305,7 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
def testNonSpecificPlatformsNamespace(self):
|
def testNonSpecificPlatformsNamespace(self):
|
||||||
schema = idl_schema.Load('test/idl_namespace_non_specific_platforms.idl')[0]
|
schema = idl_schema.Load('test/idl_namespace_non_specific_platforms.idl')[0]
|
||||||
self.assertEqual('idl_namespace_non_specific_platforms',
|
self.assertEqual('idl_namespace_non_specific_platforms',
|
||||||
schema['namespace'])
|
schema['namespace'])
|
||||||
expected = None
|
expected = None
|
||||||
self.assertEqual(expected, schema['platforms'])
|
self.assertEqual(expected, schema['platforms'])
|
||||||
|
|
||||||
@@ -214,17 +313,16 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
schema = idl_schema.Load('test/idl_generate_error_messages.idl')[0]
|
schema = idl_schema.Load('test/idl_generate_error_messages.idl')[0]
|
||||||
self.assertEqual('idl_generate_error_messages', schema['namespace'])
|
self.assertEqual('idl_generate_error_messages', schema['namespace'])
|
||||||
self.assertTrue(schema['compiler_options'].get('generate_error_messages',
|
self.assertTrue(schema['compiler_options'].get('generate_error_messages',
|
||||||
False))
|
False))
|
||||||
|
|
||||||
schema = idl_schema.Load('test/idl_basics.idl')[0]
|
schema = idl_schema.Load('test/idl_basics.idl')[0]
|
||||||
self.assertEqual('idl_basics', schema['namespace'])
|
self.assertEqual('idl_basics', schema['namespace'])
|
||||||
self.assertFalse(schema['compiler_options'].get('generate_error_messages',
|
self.assertFalse(schema['compiler_options'].get('generate_error_messages',
|
||||||
False))
|
False))
|
||||||
|
|
||||||
def testSpecificImplementNamespace(self):
|
def testSpecificImplementNamespace(self):
|
||||||
schema = idl_schema.Load('test/idl_namespace_specific_implement.idl')[0]
|
schema = idl_schema.Load('test/idl_namespace_specific_implement.idl')[0]
|
||||||
self.assertEqual('idl_namespace_specific_implement',
|
self.assertEqual('idl_namespace_specific_implement', schema['namespace'])
|
||||||
schema['namespace'])
|
|
||||||
expected = 'idl_namespace_specific_implement.idl'
|
expected = 'idl_namespace_specific_implement.idl'
|
||||||
self.assertEqual(expected, schema['compiler_options']['implemented_in'])
|
self.assertEqual(expected, schema['compiler_options']['implemented_in'])
|
||||||
|
|
||||||
@@ -232,39 +330,39 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
schema = idl_schema.Load(
|
schema = idl_schema.Load(
|
||||||
'test/idl_namespace_specific_implement_chromeos.idl')[0]
|
'test/idl_namespace_specific_implement_chromeos.idl')[0]
|
||||||
self.assertEqual('idl_namespace_specific_implement_chromeos',
|
self.assertEqual('idl_namespace_specific_implement_chromeos',
|
||||||
schema['namespace'])
|
schema['namespace'])
|
||||||
expected_implemented_path = 'idl_namespace_specific_implement_chromeos.idl'
|
expected_implemented_path = 'idl_namespace_specific_implement_chromeos.idl'
|
||||||
expected_platform = ['chromeos']
|
expected_platform = ['chromeos']
|
||||||
self.assertEqual(expected_implemented_path,
|
self.assertEqual(expected_implemented_path,
|
||||||
schema['compiler_options']['implemented_in'])
|
schema['compiler_options']['implemented_in'])
|
||||||
self.assertEqual(expected_platform, schema['platforms'])
|
self.assertEqual(expected_platform, schema['platforms'])
|
||||||
|
|
||||||
def testCallbackComment(self):
|
def testCallbackComment(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
self.assertEqual('A comment on a callback.',
|
self.assertEqual('A comment on a callback.',
|
||||||
getReturnsAsync(schema, 'function16')['description'])
|
getReturnsAsync(schema, 'function16')['description'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'A parameter.',
|
'A parameter.',
|
||||||
getReturnsAsync(schema, 'function16')['parameters'][0]['description'])
|
getReturnsAsync(schema, 'function16')['parameters'][0]['description'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'Just a parameter comment, with no comment on the callback.',
|
'Just a parameter comment, with no comment on the callback.',
|
||||||
getReturnsAsync(schema, 'function17')['parameters'][0]['description'])
|
getReturnsAsync(schema, 'function17')['parameters'][0]['description'])
|
||||||
self.assertEqual(
|
self.assertEqual('Override callback comment.',
|
||||||
'Override callback comment.',
|
getReturnsAsync(schema, 'function18')['description'])
|
||||||
getReturnsAsync(schema, 'function18')['description'])
|
|
||||||
|
|
||||||
def testFunctionComment(self):
|
def testFunctionComment(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
func = getFunction(schema, 'function3')
|
func = getFunction(schema, 'function3')
|
||||||
self.assertEqual(('This comment should appear in the documentation, '
|
self.assertEqual(('This comment should appear in the documentation, '
|
||||||
'despite occupying multiple lines.'),
|
'despite occupying multiple lines.'), func['description'])
|
||||||
func['description'])
|
self.assertEqual([{
|
||||||
self.assertEqual(
|
'description': ('So should this comment about the argument. '
|
||||||
[{'description': ('So should this comment about the argument. '
|
'<em>HTML</em> is fine too.'),
|
||||||
'<em>HTML</em> is fine too.'),
|
'name':
|
||||||
'name': 'arg',
|
'arg',
|
||||||
'$ref': 'MyType1'}],
|
'$ref':
|
||||||
func['parameters'])
|
'MyType1'
|
||||||
|
}], func['parameters'])
|
||||||
func = getFunction(schema, 'function4')
|
func = getFunction(schema, 'function4')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'<p>This tests if "double-quotes" are escaped correctly.</p>'
|
'<p>This tests if "double-quotes" are escaped correctly.</p>'
|
||||||
@@ -275,12 +373,18 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
schema = idl_schema.Load('test/idl_reserved_words.idl')[0]
|
schema = idl_schema.Load('test/idl_reserved_words.idl')[0]
|
||||||
|
|
||||||
foo_type = getType(schema, 'Foo')
|
foo_type = getType(schema, 'Foo')
|
||||||
self.assertEqual([{'name': 'float'}, {'name': 'DOMString'}],
|
self.assertEqual([{
|
||||||
foo_type['enum'])
|
'name': 'float'
|
||||||
|
}, {
|
||||||
|
'name': 'DOMString'
|
||||||
|
}], foo_type['enum'])
|
||||||
|
|
||||||
enum_type = getType(schema, 'enum')
|
enum_type = getType(schema, 'enum')
|
||||||
self.assertEqual([{'name': 'callback'}, {'name': 'namespace'}],
|
self.assertEqual([{
|
||||||
enum_type['enum'])
|
'name': 'callback'
|
||||||
|
}, {
|
||||||
|
'name': 'namespace'
|
||||||
|
}], enum_type['enum'])
|
||||||
|
|
||||||
dictionary = getType(schema, 'dictionary')
|
dictionary = getType(schema, 'dictionary')
|
||||||
self.assertEqual('integer', dictionary['properties']['long']['type'])
|
self.assertEqual('integer', dictionary['properties']['long']['type'])
|
||||||
@@ -300,12 +404,10 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
self.assertEqual('integer', foo_type['properties']['x']['type'])
|
self.assertEqual('integer', foo_type['properties']['x']['type'])
|
||||||
self.assertEqual('object', foo_type['properties']['y']['type'])
|
self.assertEqual('object', foo_type['properties']['y']['type'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'any',
|
'any', foo_type['properties']['y']['additionalProperties']['type'])
|
||||||
foo_type['properties']['y']['additionalProperties']['type'])
|
|
||||||
self.assertEqual('object', foo_type['properties']['z']['type'])
|
self.assertEqual('object', foo_type['properties']['z']['type'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'any',
|
'any', foo_type['properties']['z']['additionalProperties']['type'])
|
||||||
foo_type['properties']['z']['additionalProperties']['type'])
|
|
||||||
self.assertEqual('Window', foo_type['properties']['z']['isInstanceOf'])
|
self.assertEqual('Window', foo_type['properties']['z']['isInstanceOf'])
|
||||||
|
|
||||||
bar_type = getType(schema, 'BarType')
|
bar_type = getType(schema, 'BarType')
|
||||||
@@ -337,35 +439,48 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
|
|
||||||
union_type = getType(schema, 'UnionType')
|
union_type = getType(schema, 'UnionType')
|
||||||
expected = {
|
expected = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'id': 'UnionType',
|
'id': 'UnionType',
|
||||||
'properties': {
|
'properties': {
|
||||||
'x': {
|
'x': {
|
||||||
'name': 'x',
|
'name': 'x',
|
||||||
'optional': True,
|
'optional': True,
|
||||||
'choices': [
|
'choices': [
|
||||||
{'type': 'integer'},
|
{
|
||||||
{'$ref': 'FooType'},
|
'type': 'integer'
|
||||||
]
|
},
|
||||||
},
|
{
|
||||||
'y': {
|
'$ref': 'FooType'
|
||||||
'name': 'y',
|
},
|
||||||
'choices': [
|
]
|
||||||
{'type': 'string'},
|
},
|
||||||
{'type': 'object',
|
'y': {
|
||||||
'additionalProperties': {'type': 'any'}}
|
'name':
|
||||||
]
|
'y',
|
||||||
},
|
'choices': [{
|
||||||
'z': {
|
'type': 'string'
|
||||||
'name': 'z',
|
}, {
|
||||||
'choices': [
|
'type': 'object',
|
||||||
{'type': 'object', 'isInstanceOf': 'ImageData',
|
'additionalProperties': {
|
||||||
'additionalProperties': {'type': 'any'}},
|
'type': 'any'
|
||||||
{'type': 'integer'}
|
}
|
||||||
]
|
}]
|
||||||
}
|
},
|
||||||
},
|
'z': {
|
||||||
}
|
'name':
|
||||||
|
'z',
|
||||||
|
'choices': [{
|
||||||
|
'type': 'object',
|
||||||
|
'isInstanceOf': 'ImageData',
|
||||||
|
'additionalProperties': {
|
||||||
|
'type': 'any'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
'type': 'integer'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
self.assertEqual(expected, union_type)
|
self.assertEqual(expected, union_type)
|
||||||
|
|
||||||
@@ -374,19 +489,20 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
|
|
||||||
union_type = getType(schema, 'ModifiedUnionType')
|
union_type = getType(schema, 'ModifiedUnionType')
|
||||||
expected = {
|
expected = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'id': 'ModifiedUnionType',
|
'id': 'ModifiedUnionType',
|
||||||
'properties': {
|
'properties': {
|
||||||
'x': {
|
'x': {
|
||||||
'name': 'x',
|
'name': 'x',
|
||||||
'nodoc': True,
|
'nodoc': True,
|
||||||
'choices': [
|
'choices': [{
|
||||||
{'type': 'integer'},
|
'type': 'integer'
|
||||||
{'type': 'string'}
|
}, {
|
||||||
]
|
'type': 'string'
|
||||||
}
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.assertEqual(expected, union_type)
|
self.assertEqual(expected, union_type)
|
||||||
|
|
||||||
@@ -394,17 +510,17 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
schema = idl_schema.Load('test/idl_object_types.idl')[0]
|
schema = idl_schema.Load('test/idl_object_types.idl')[0]
|
||||||
object_type = getType(schema, 'SerializableFunctionObject')
|
object_type = getType(schema, 'SerializableFunctionObject')
|
||||||
expected = {
|
expected = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'id': 'SerializableFunctionObject',
|
'id': 'SerializableFunctionObject',
|
||||||
'properties': {
|
'properties': {
|
||||||
'func': {
|
'func': {
|
||||||
'name': 'func',
|
'name': 'func',
|
||||||
'serializableFunction': True,
|
'serializableFunction': True,
|
||||||
'type': 'function',
|
'type': 'function',
|
||||||
'parameters': []
|
'parameters': []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.assertEqual(expected, object_type)
|
self.assertEqual(expected, object_type)
|
||||||
|
|
||||||
def testUnionsWithFunctions(self):
|
def testUnionsWithFunctions(self):
|
||||||
@@ -412,12 +528,13 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
|
|
||||||
union_params = getParams(schema, 'union_params')
|
union_params = getParams(schema, 'union_params')
|
||||||
expected = [{
|
expected = [{
|
||||||
'name': 'x',
|
'name': 'x',
|
||||||
'choices': [
|
'choices': [{
|
||||||
{'type': 'integer'},
|
'type': 'integer'
|
||||||
{'type': 'string'}
|
}, {
|
||||||
]
|
'type': 'string'
|
||||||
}]
|
}]
|
||||||
|
}]
|
||||||
|
|
||||||
self.assertEqual(expected, union_params)
|
self.assertEqual(expected, union_params)
|
||||||
|
|
||||||
@@ -426,24 +543,32 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
|
|
||||||
blah_params = getReturnsAsync(schema, 'blah')
|
blah_params = getReturnsAsync(schema, 'blah')
|
||||||
expected = {
|
expected = {
|
||||||
'name': 'callback', 'parameters': [{
|
'name':
|
||||||
'name': 'x',
|
'callback',
|
||||||
'choices': [
|
'parameters': [{
|
||||||
{'type': 'integer'},
|
'name': 'x',
|
||||||
{'type': 'string'}
|
'choices': [{
|
||||||
]}
|
'type': 'integer'
|
||||||
]
|
}, {
|
||||||
}
|
'type': 'string'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
badabish_params = getReturnsAsync(schema, 'badabish')
|
badabish_params = getReturnsAsync(schema, 'badabish')
|
||||||
expected = {
|
expected = {
|
||||||
'name': 'callback', 'parameters': [{
|
'name':
|
||||||
'name': 'x', 'optional': True, 'choices': [
|
'callback',
|
||||||
{'type': 'integer'},
|
'parameters': [{
|
||||||
{'type': 'string'}
|
'name': 'x',
|
||||||
]
|
'optional': True,
|
||||||
}]
|
'choices': [{
|
||||||
}
|
'type': 'integer'
|
||||||
|
}, {
|
||||||
|
'type': 'string'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
self.assertEqual(expected, badabish_params)
|
self.assertEqual(expected, badabish_params)
|
||||||
|
|
||||||
@@ -453,7 +578,10 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
expected_params = []
|
expected_params = []
|
||||||
expected_returns_async = {
|
expected_returns_async = {
|
||||||
'name': 'callback',
|
'name': 'callback',
|
||||||
'parameters': [{'name': 'x', 'type': 'integer'}],
|
'parameters': [{
|
||||||
|
'name': 'x',
|
||||||
|
'type': 'integer'
|
||||||
|
}],
|
||||||
'does_not_support_promises': 'Test'
|
'does_not_support_promises': 'Test'
|
||||||
}
|
}
|
||||||
params = getParams(schema, 'non_promise_supporting')
|
params = getParams(schema, 'non_promise_supporting')
|
||||||
@@ -465,46 +593,60 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
def testFunctionWithoutPromiseSupportAndParams(self):
|
def testFunctionWithoutPromiseSupportAndParams(self):
|
||||||
schema = idl_schema.Load('test/idl_function_types.idl')[0]
|
schema = idl_schema.Load('test/idl_function_types.idl')[0]
|
||||||
|
|
||||||
expected_params = [
|
expected_params = [{
|
||||||
{'name': 'z', 'type': 'integer'},
|
'name': 'z',
|
||||||
{'name': 'y', 'choices': [{'type': 'integer'}, {'type': 'string'}]}
|
'type': 'integer'
|
||||||
]
|
}, {
|
||||||
|
'name': 'y',
|
||||||
|
'choices': [{
|
||||||
|
'type': 'integer'
|
||||||
|
}, {
|
||||||
|
'type': 'string'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
expected_returns_async = {
|
expected_returns_async = {
|
||||||
'name': 'callback',
|
'name': 'callback',
|
||||||
'parameters': [{'name': 'x', 'type': 'integer'}],
|
'parameters': [{
|
||||||
|
'name': 'x',
|
||||||
|
'type': 'integer'
|
||||||
|
}],
|
||||||
'does_not_support_promises': 'Test'
|
'does_not_support_promises': 'Test'
|
||||||
}
|
}
|
||||||
params = getParams(schema, 'non_promise_supporting_with_params')
|
params = getParams(schema, 'non_promise_supporting_with_params')
|
||||||
returns_async = getReturnsAsync(
|
returns_async = getReturnsAsync(schema,
|
||||||
schema, 'non_promise_supporting_with_params'
|
'non_promise_supporting_with_params')
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(expected_params, params)
|
self.assertEqual(expected_params, params)
|
||||||
self.assertEqual(expected_returns_async, returns_async)
|
self.assertEqual(expected_returns_async, returns_async)
|
||||||
|
|
||||||
def testProperties(self):
|
def testProperties(self):
|
||||||
schema = idl_schema.Load('test/idl_properties.idl')[0]
|
schema = idl_schema.Load('test/idl_properties.idl')[0]
|
||||||
self.assertEqual(OrderedDict([
|
self.assertEqual(
|
||||||
('first', OrderedDict([
|
OrderedDict([
|
||||||
('description', 'Integer property.'),
|
('first',
|
||||||
('type', 'integer'),
|
OrderedDict([
|
||||||
('value', 42),
|
('description', 'Integer property.'),
|
||||||
])),
|
('type', 'integer'),
|
||||||
('second', OrderedDict([
|
('value', 42),
|
||||||
('description', 'Double property.'),
|
])),
|
||||||
('type', 'number'),
|
('second',
|
||||||
('value', 42.1),
|
OrderedDict([
|
||||||
])),
|
('description', 'Double property.'),
|
||||||
('third', OrderedDict([
|
('type', 'number'),
|
||||||
('description', 'String property.'),
|
('value', 42.1),
|
||||||
('type', 'string'),
|
])),
|
||||||
('value', 'hello world'),
|
('third',
|
||||||
])),
|
OrderedDict([
|
||||||
('fourth', OrderedDict([
|
('description', 'String property.'),
|
||||||
('description', 'Unvalued property.'),
|
('type', 'string'),
|
||||||
('type', 'integer'),
|
('value', 'hello world'),
|
||||||
])),
|
])),
|
||||||
]), schema.get('properties'))
|
('fourth',
|
||||||
|
OrderedDict([
|
||||||
|
('description', 'Unvalued property.'),
|
||||||
|
('type', 'integer'),
|
||||||
|
])),
|
||||||
|
]), schema.get('properties'))
|
||||||
|
|
||||||
def testManifestKeys(self):
|
def testManifestKeys(self):
|
||||||
schema = self.idl_basics
|
schema = self.idl_basics
|
||||||
@@ -512,19 +654,20 @@ class IdlSchemaTest(unittest.TestCase):
|
|||||||
# exhaustive so we don't have to update it each time we add a new key in the
|
# exhaustive so we don't have to update it each time we add a new key in the
|
||||||
# test file.
|
# test file.
|
||||||
manifest_keys = schema.get('manifest_keys')
|
manifest_keys = schema.get('manifest_keys')
|
||||||
self.assertEqual(manifest_keys['key_str'],
|
self.assertEqual(
|
||||||
OrderedDict([('description', 'String manifest key.'),
|
manifest_keys['key_str'],
|
||||||
('name', 'key_str'),
|
OrderedDict([('description', 'String manifest key.'),
|
||||||
('type', 'string')]))
|
('name', 'key_str'), ('type', 'string')]))
|
||||||
self.assertEqual(manifest_keys['key_ref'],
|
self.assertEqual(manifest_keys['key_ref'],
|
||||||
OrderedDict([('name', 'key_ref'),
|
OrderedDict([('name', 'key_ref'), ('$ref', 'MyType2')])),
|
||||||
('$ref', 'MyType2')])),
|
self.assertEqual(
|
||||||
self.assertEqual(manifest_keys['choice_with_arrays'],
|
manifest_keys['choice_with_arrays'],
|
||||||
OrderedDict([('name', 'choice_with_arrays'),
|
OrderedDict([('name', 'choice_with_arrays'),
|
||||||
('$ref', 'ChoiceWithArraysType')])),
|
('$ref', 'ChoiceWithArraysType')])),
|
||||||
self.assertEqual(manifest_keys['choice_with_optional'],
|
self.assertEqual(
|
||||||
OrderedDict([('name', 'choice_with_optional'),
|
manifest_keys['choice_with_optional'],
|
||||||
('$ref', 'ChoiceWithOptionalType')]))
|
OrderedDict([('name', 'choice_with_optional'),
|
||||||
|
('$ref', 'ChoiceWithOptionalType')]))
|
||||||
|
|
||||||
def testNoManifestKeys(self):
|
def testNoManifestKeys(self):
|
||||||
schema = idl_schema.Load('test/idl_properties.idl')[0]
|
schema = idl_schema.Load('test/idl_properties.idl')[0]
|
||||||
|
@@ -23,11 +23,15 @@ NOTE = """// NOTE: The format of types has changed. 'FooType' is now
|
|||||||
// See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
|
// See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class JsExternsGenerator(object):
|
class JsExternsGenerator(object):
|
||||||
|
|
||||||
def Generate(self, namespace):
|
def Generate(self, namespace):
|
||||||
return _Generator(namespace).Generate()
|
return _Generator(namespace).Generate()
|
||||||
|
|
||||||
|
|
||||||
class _Generator(object):
|
class _Generator(object):
|
||||||
|
|
||||||
def __init__(self, namespace):
|
def __init__(self, namespace):
|
||||||
self._namespace = namespace
|
self._namespace = namespace
|
||||||
self._class_name = None
|
self._class_name = None
|
||||||
@@ -45,7 +49,7 @@ class _Generator(object):
|
|||||||
src_to_script = os.path.relpath(script_dir, src_root)
|
src_to_script = os.path.relpath(script_dir, src_root)
|
||||||
# tools/json_schema_compiler/compiler.py
|
# tools/json_schema_compiler/compiler.py
|
||||||
compiler_path = os.path.join(src_to_script, 'compiler.py')
|
compiler_path = os.path.join(src_to_script, 'compiler.py')
|
||||||
(c.Append(self._GetHeader(compiler_path, self._namespace.name))
|
(c.Append(self._GetHeader(compiler_path, self._namespace.name)) \
|
||||||
.Append())
|
.Append())
|
||||||
|
|
||||||
self._AppendNamespaceObject(c)
|
self._AppendNamespaceObject(c)
|
||||||
@@ -69,13 +73,10 @@ class _Generator(object):
|
|||||||
def _GetHeader(self, tool, namespace):
|
def _GetHeader(self, tool, namespace):
|
||||||
"""Returns the file header text.
|
"""Returns the file header text.
|
||||||
"""
|
"""
|
||||||
return (self._js_util.GetLicense() + '\n' +
|
return (self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) +
|
||||||
self._js_util.GetInfo(tool) + (NOTE % namespace) + '\n' +
|
(NOTE % namespace) + '\n' + '/**\n' +
|
||||||
'/**\n' +
|
|
||||||
(' * @fileoverview Externs generated from namespace: %s\n' %
|
(' * @fileoverview Externs generated from namespace: %s\n' %
|
||||||
namespace) +
|
namespace) + ' * @externs\n' + ' */')
|
||||||
' * @externs\n' +
|
|
||||||
' */')
|
|
||||||
|
|
||||||
def _AppendType(self, c, js_type):
|
def _AppendType(self, c, js_type):
|
||||||
"""Given a Type object, generates the Code for this type's definition.
|
"""Given a Type object, generates the Code for this type's definition.
|
||||||
@@ -95,9 +96,10 @@ class _Generator(object):
|
|||||||
js_type.simple_name)
|
js_type.simple_name)
|
||||||
c.Eblock(' */')
|
c.Eblock(' */')
|
||||||
c.Append('%s.%s = {' % (self._GetNamespace(), js_type.name))
|
c.Append('%s.%s = {' % (self._GetNamespace(), js_type.name))
|
||||||
c.Append('\n'.join(
|
c.Append('\n'.join([
|
||||||
[" %s: '%s'," % (self._js_util.GetPropertyName(v.name), v.name)
|
" %s: '%s'," % (self._js_util.GetPropertyName(v.name), v.name)
|
||||||
for v in js_type.enum_values]))
|
for v in js_type.enum_values
|
||||||
|
]))
|
||||||
c.Append('};')
|
c.Append('};')
|
||||||
|
|
||||||
def _IsTypeConstructor(self, js_type):
|
def _IsTypeConstructor(self, js_type):
|
||||||
@@ -120,8 +122,8 @@ class _Generator(object):
|
|||||||
if js_type.property_type is not PropertyType.OBJECT:
|
if js_type.property_type is not PropertyType.OBJECT:
|
||||||
self._js_util.AppendTypeJsDoc(c, self._namespace.name, js_type, optional)
|
self._js_util.AppendTypeJsDoc(c, self._namespace.name, js_type, optional)
|
||||||
elif is_constructor:
|
elif is_constructor:
|
||||||
c.Comment('@constructor', comment_prefix = '', wrap_indent=4)
|
c.Comment('@constructor', comment_prefix='', wrap_indent=4)
|
||||||
c.Comment('@private', comment_prefix = '', wrap_indent=4)
|
c.Comment('@private', comment_prefix='', wrap_indent=4)
|
||||||
else:
|
else:
|
||||||
self._AppendTypedef(c, js_type.properties)
|
self._AppendTypedef(c, js_type.properties)
|
||||||
|
|
||||||
@@ -151,8 +153,10 @@ class _Generator(object):
|
|||||||
|
|
||||||
c.Append('@typedef {')
|
c.Append('@typedef {')
|
||||||
if properties:
|
if properties:
|
||||||
self._js_util.AppendObjectDefinition(
|
self._js_util.AppendObjectDefinition(c,
|
||||||
c, self._namespace.name, properties, new_line=False)
|
self._namespace.name,
|
||||||
|
properties,
|
||||||
|
new_line=False)
|
||||||
else:
|
else:
|
||||||
c.Append('Object', new_line=False)
|
c.Append('Object', new_line=False)
|
||||||
c.Append('}', new_line=False)
|
c.Append('}', new_line=False)
|
||||||
@@ -178,8 +182,8 @@ class _Generator(object):
|
|||||||
"""
|
"""
|
||||||
self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function)
|
self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function)
|
||||||
params = self._GetFunctionParams(function)
|
params = self._GetFunctionParams(function)
|
||||||
c.Append('%s.%s = function(%s) {};' % (self._GetNamespace(),
|
c.Append('%s.%s = function(%s) {};' %
|
||||||
function.name, params))
|
(self._GetNamespace(), function.name, params))
|
||||||
c.Append()
|
c.Append()
|
||||||
|
|
||||||
def _AppendEvent(self, c, event):
|
def _AppendEvent(self, c, event):
|
||||||
|
@@ -496,15 +496,16 @@ chrome.fakeJson.funcWithReturnsAsync = function(someNumber, callback) {};""" % (
|
|||||||
|
|
||||||
|
|
||||||
class JsExternGeneratorTest(unittest.TestCase):
|
class JsExternGeneratorTest(unittest.TestCase):
|
||||||
|
|
||||||
def _GetNamespace(self, fake_content, filename, is_idl):
|
def _GetNamespace(self, fake_content, filename, is_idl):
|
||||||
"""Returns a namespace object for the given content"""
|
"""Returns a namespace object for the given content"""
|
||||||
api_def = (idl_schema.Process(fake_content, filename) if is_idl
|
api_def = (idl_schema.Process(fake_content, filename)
|
||||||
else json_parse.Parse(fake_content))
|
if is_idl else json_parse.Parse(fake_content))
|
||||||
m = model.Model()
|
m = model.Model()
|
||||||
return m.AddNamespace(api_def[0], filename)
|
return m.AddNamespace(api_def[0], filename)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.maxDiff = None # Lets us see the full diff when inequal.
|
self.maxDiff = None # Lets us see the full diff when inequal.
|
||||||
|
|
||||||
def testBasic(self):
|
def testBasic(self):
|
||||||
namespace = self._GetNamespace(fake_idl, 'fake_api.idl', True)
|
namespace = self._GetNamespace(fake_idl, 'fake_api.idl', True)
|
||||||
|
@@ -15,11 +15,15 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class JsInterfaceGenerator(object):
|
class JsInterfaceGenerator(object):
|
||||||
|
|
||||||
def Generate(self, namespace):
|
def Generate(self, namespace):
|
||||||
return _Generator(namespace).Generate()
|
return _Generator(namespace).Generate()
|
||||||
|
|
||||||
|
|
||||||
class _Generator(object):
|
class _Generator(object):
|
||||||
|
|
||||||
def __init__(self, namespace):
|
def __init__(self, namespace):
|
||||||
self._namespace = namespace
|
self._namespace = namespace
|
||||||
first = namespace.name[0].upper()
|
first = namespace.name[0].upper()
|
||||||
@@ -31,7 +35,7 @@ class _Generator(object):
|
|||||||
"""Generates a Code object with the schema for the entire namespace.
|
"""Generates a Code object with the schema for the entire namespace.
|
||||||
"""
|
"""
|
||||||
c = Code()
|
c = Code()
|
||||||
(c.Append(self._GetHeader(sys.argv[0], self._namespace.name))
|
(c.Append(self._GetHeader(sys.argv[0], self._namespace.name)) \
|
||||||
.Append())
|
.Append())
|
||||||
|
|
||||||
self._AppendInterfaceObject(c)
|
self._AppendInterfaceObject(c)
|
||||||
@@ -56,10 +60,10 @@ class _Generator(object):
|
|||||||
def _GetHeader(self, tool, namespace):
|
def _GetHeader(self, tool, namespace):
|
||||||
"""Returns the file header text.
|
"""Returns the file header text.
|
||||||
"""
|
"""
|
||||||
return (
|
return (self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) +
|
||||||
self._js_util.GetLicense() + '\n' + self._js_util.GetInfo(tool) + '\n' +
|
'\n' +
|
||||||
('/** @fileoverview Interface for %s that can be overriden. */' %
|
('/** @fileoverview Interface for %s that can be overriden. */' %
|
||||||
namespace))
|
namespace))
|
||||||
|
|
||||||
def _AppendInterfaceObject(self, c):
|
def _AppendInterfaceObject(self, c):
|
||||||
"""Appends the code creating the interface object.
|
"""Appends the code creating the interface object.
|
||||||
@@ -67,7 +71,7 @@ class _Generator(object):
|
|||||||
/** @interface */
|
/** @interface */
|
||||||
function SettingsPrivate() {}
|
function SettingsPrivate() {}
|
||||||
"""
|
"""
|
||||||
(c.Append('/** @interface */')
|
(c.Append('/** @interface */') \
|
||||||
.Append('function %s() {}' % self._interface))
|
.Append('function %s() {}' % self._interface))
|
||||||
|
|
||||||
def _AppendFunction(self, c, function):
|
def _AppendFunction(self, c, function):
|
||||||
@@ -88,8 +92,8 @@ class _Generator(object):
|
|||||||
|
|
||||||
self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function)
|
self._js_util.AppendFunctionJsDoc(c, self._namespace.name, function)
|
||||||
|
|
||||||
c.Append('%s: function(%s) {},' % (function.name, ', '.join(
|
c.Append('%s: function(%s) {},' %
|
||||||
getParamNames(function))))
|
(function.name, ', '.join(getParamNames(function))))
|
||||||
c.Append()
|
c.Append()
|
||||||
|
|
||||||
def _AppendEvent(self, c, event):
|
def _AppendEvent(self, c, event):
|
||||||
|
@@ -110,10 +110,12 @@ FakeApi.prototype = {
|
|||||||
* @type {!ChromeEvent}
|
* @type {!ChromeEvent}
|
||||||
* @see https://developer.chrome.com/extensions/fakeApi#event-onTrapDetected
|
* @see https://developer.chrome.com/extensions/fakeApi#event-onTrapDetected
|
||||||
*/
|
*/
|
||||||
FakeApi.prototype.onTrapDetected;""" % (datetime.now().year,
|
FakeApi.prototype.onTrapDetected;""" %
|
||||||
sys.argv[0].replace('\\', '/')))
|
(datetime.now().year, sys.argv[0].replace('\\', '/')))
|
||||||
|
|
||||||
|
|
||||||
class JsExternGeneratorTest(unittest.TestCase):
|
class JsExternGeneratorTest(unittest.TestCase):
|
||||||
|
|
||||||
def _GetNamespace(self, fake_content, filename):
|
def _GetNamespace(self, fake_content, filename):
|
||||||
"""Returns a namespace object for the given content"""
|
"""Returns a namespace object for the given content"""
|
||||||
api_def = idl_schema.Process(fake_content, filename)
|
api_def = idl_schema.Process(fake_content, filename)
|
||||||
@@ -121,7 +123,7 @@ class JsExternGeneratorTest(unittest.TestCase):
|
|||||||
return m.AddNamespace(api_def[0], filename)
|
return m.AddNamespace(api_def[0], filename)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.maxDiff = None # Lets us see the full diff when inequal.
|
self.maxDiff = None # Lets us see the full diff when inequal.
|
||||||
|
|
||||||
def testBasic(self):
|
def testBasic(self):
|
||||||
namespace = self._GetNamespace(fake_idl, 'fake_api.idl')
|
namespace = self._GetNamespace(fake_idl, 'fake_api.idl')
|
||||||
|
@@ -21,6 +21,7 @@ INFO = """// This file was generated by:
|
|||||||
class JsUtil(object):
|
class JsUtil(object):
|
||||||
"""A helper class for generating JS Code.
|
"""A helper class for generating JS Code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def GetLicense(self):
|
def GetLicense(self):
|
||||||
"""Returns the license text for JS extern and interface files.
|
"""Returns the license text for JS extern and interface files.
|
||||||
"""
|
"""
|
||||||
@@ -31,7 +32,7 @@ class JsUtil(object):
|
|||||||
"""
|
"""
|
||||||
return (INFO % tool.replace('\\', '/'))
|
return (INFO % tool.replace('\\', '/'))
|
||||||
|
|
||||||
def GetPropertyName(self,e):
|
def GetPropertyName(self, e):
|
||||||
# Enum properties are normified to be in ALL_CAPS_STYLE.
|
# Enum properties are normified to be in ALL_CAPS_STYLE.
|
||||||
# Assume enum '1ring-rulesThemAll'.
|
# Assume enum '1ring-rulesThemAll'.
|
||||||
# Transform to '1ring-rules_Them_All'.
|
# Transform to '1ring-rules_Them_All'.
|
||||||
@@ -43,8 +44,11 @@ class JsUtil(object):
|
|||||||
# Transform to '_1RING_RULES_THEM_ALL'.
|
# Transform to '_1RING_RULES_THEM_ALL'.
|
||||||
return e.upper()
|
return e.upper()
|
||||||
|
|
||||||
def AppendObjectDefinition(self, c, namespace_name, properties,
|
def AppendObjectDefinition(self,
|
||||||
new_line=True):
|
c,
|
||||||
|
namespace_name,
|
||||||
|
properties,
|
||||||
|
new_line=True):
|
||||||
"""Given an OrderedDict of properties, returns a Code containing the
|
"""Given an OrderedDict of properties, returns a Code containing the
|
||||||
description of an object.
|
description of an object.
|
||||||
"""
|
"""
|
||||||
@@ -62,8 +66,8 @@ class JsUtil(object):
|
|||||||
first = False
|
first = False
|
||||||
js_type = self._TypeToJsType(namespace_name, prop.type_)
|
js_type = self._TypeToJsType(namespace_name, prop.type_)
|
||||||
if prop.optional:
|
if prop.optional:
|
||||||
js_type = (Code().Append('(')
|
js_type = (Code().Append('(') \
|
||||||
.Concat(js_type, new_line=False)
|
.Concat(js_type, new_line=False) \
|
||||||
.Append('|undefined)', new_line=False))
|
.Append('|undefined)', new_line=False))
|
||||||
c.Append('%s: ' % field, strip_right=False)
|
c.Append('%s: ' % field, strip_right=False)
|
||||||
c.Concat(js_type, new_line=False)
|
c.Concat(js_type, new_line=False)
|
||||||
@@ -88,16 +92,18 @@ class JsUtil(object):
|
|||||||
c.Append('}', new_line=False)
|
c.Append('}', new_line=False)
|
||||||
c.Comment(' %s' % name, comment_prefix='', wrap_indent=4, new_line=False)
|
c.Comment(' %s' % name, comment_prefix='', wrap_indent=4, new_line=False)
|
||||||
if description:
|
if description:
|
||||||
c.Comment(' %s' % description, comment_prefix='',
|
c.Comment(' %s' % description,
|
||||||
wrap_indent=4, new_line=False)
|
comment_prefix='',
|
||||||
|
wrap_indent=4,
|
||||||
|
new_line=False)
|
||||||
|
|
||||||
for i, param in enumerate(function.params):
|
for i, param in enumerate(function.params):
|
||||||
# Mark the parameter as optional, *only if* all following parameters are
|
# Mark the parameter as optional, *only if* all following parameters are
|
||||||
# also optional, to avoid JSC_OPTIONAL_ARG_AT_END errors thrown by Closure
|
# also optional, to avoid JSC_OPTIONAL_ARG_AT_END errors thrown by Closure
|
||||||
# Compiler.
|
# Compiler.
|
||||||
optional = (
|
optional = (all(p.optional for p in function.params[i:])
|
||||||
all(p.optional for p in function.params[i:]) and
|
and (function.returns_async is None
|
||||||
(function.returns_async is None or function.returns_async.optional))
|
or function.returns_async.optional))
|
||||||
js_type = self._TypeToJsType(namespace_name, param.type_)
|
js_type = self._TypeToJsType(namespace_name, param.type_)
|
||||||
|
|
||||||
# If the parameter was originally optional, but was followed by
|
# If the parameter was originally optional, but was followed by
|
||||||
@@ -123,8 +129,8 @@ class JsUtil(object):
|
|||||||
|
|
||||||
if function.returns:
|
if function.returns:
|
||||||
append_field(c, 'return',
|
append_field(c, 'return',
|
||||||
self._TypeToJsType(namespace_name, function.returns),
|
self._TypeToJsType(namespace_name, function.returns), '',
|
||||||
'', False, function.returns.description)
|
False, function.returns.description)
|
||||||
|
|
||||||
if function.deprecated:
|
if function.deprecated:
|
||||||
c.Append('@deprecated %s' % function.deprecated)
|
c.Append('@deprecated %s' % function.deprecated)
|
||||||
@@ -149,9 +155,8 @@ class JsUtil(object):
|
|||||||
"""Converts a model.Function to a JS type (i.e., function([params])...)"""
|
"""Converts a model.Function to a JS type (i.e., function([params])...)"""
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Append('function(')
|
c.Append('function(')
|
||||||
c.Concat(
|
c.Concat(self._FunctionParamsToJsParams(namespace_name, function.params),
|
||||||
self._FunctionParamsToJsParams(namespace_name, function.params),
|
new_line=False)
|
||||||
new_line=False)
|
|
||||||
c.Append('): ', new_line=False, strip_right=False)
|
c.Append('): ', new_line=False, strip_right=False)
|
||||||
|
|
||||||
if function.returns:
|
if function.returns:
|
||||||
@@ -169,9 +174,9 @@ class JsUtil(object):
|
|||||||
# appended to the params as a callback.
|
# appended to the params as a callback.
|
||||||
c = Code()
|
c = Code()
|
||||||
c.Append('function(')
|
c.Append('function(')
|
||||||
c.Concat(
|
c.Concat(self._FunctionParamsToJsParams(namespace_name,
|
||||||
self._FunctionParamsToJsParams(namespace_name, returns_async.params),
|
returns_async.params),
|
||||||
new_line=False)
|
new_line=False)
|
||||||
c.Append('): ', new_line=False, strip_right=False)
|
c.Append('): ', new_line=False, strip_right=False)
|
||||||
|
|
||||||
c.Append('void', new_line=False)
|
c.Append('void', new_line=False)
|
||||||
@@ -211,10 +216,9 @@ class JsUtil(object):
|
|||||||
return Code().Append(js_type.instance_of)
|
return Code().Append(js_type.instance_of)
|
||||||
return Code().Append('Object')
|
return Code().Append('Object')
|
||||||
if js_type.property_type is PropertyType.ARRAY:
|
if js_type.property_type is PropertyType.ARRAY:
|
||||||
return (Code().Append('!Array<').
|
return (Code().Append('!Array<').Concat(
|
||||||
Concat(self._TypeToJsType(namespace_name, js_type.item_type),
|
self._TypeToJsType(namespace_name, js_type.item_type),
|
||||||
new_line=False).
|
new_line=False).Append('>', new_line=False))
|
||||||
Append('>', new_line=False))
|
|
||||||
if js_type.property_type is PropertyType.REF:
|
if js_type.property_type is PropertyType.REF:
|
||||||
ref_type = '!chrome.%s.%s' % (namespace_name, js_type.ref_type)
|
ref_type = '!chrome.%s.%s' % (namespace_name, js_type.ref_type)
|
||||||
return Code().Append(ref_type)
|
return Code().Append(ref_type)
|
||||||
@@ -235,7 +239,7 @@ class JsUtil(object):
|
|||||||
return Code().Append('*')
|
return Code().Append('*')
|
||||||
if js_type.property_type.is_fundamental:
|
if js_type.property_type.is_fundamental:
|
||||||
return Code().Append(js_type.property_type.name)
|
return Code().Append(js_type.property_type.name)
|
||||||
return Code().Append('?') # TODO(tbreisacher): Make this more specific.
|
return Code().Append('?') # TODO(tbreisacher): Make this more specific.
|
||||||
|
|
||||||
def AppendSeeLink(self, c, namespace_name, object_type, object_name):
|
def AppendSeeLink(self, c, namespace_name, object_type, object_name):
|
||||||
"""Appends a @see link for a given API 'object' (type, method, or event).
|
"""Appends a @see link for a given API 'object' (type, method, or event).
|
||||||
|
@@ -9,8 +9,8 @@ import sys
|
|||||||
_FILE_PATH = os.path.dirname(os.path.realpath(__file__))
|
_FILE_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||||
_SYS_PATH = sys.path[:]
|
_SYS_PATH = sys.path[:]
|
||||||
try:
|
try:
|
||||||
_COMMENT_EATER_PATH = os.path.join(
|
_COMMENT_EATER_PATH = os.path.join(_FILE_PATH, os.pardir,
|
||||||
_FILE_PATH, os.pardir, 'json_comment_eater')
|
'json_comment_eater')
|
||||||
sys.path.insert(0, _COMMENT_EATER_PATH)
|
sys.path.insert(0, _COMMENT_EATER_PATH)
|
||||||
import json_comment_eater
|
import json_comment_eater
|
||||||
finally:
|
finally:
|
||||||
@@ -36,17 +36,14 @@ except ImportError:
|
|||||||
|
|
||||||
_SYS_PATH = sys.path[:]
|
_SYS_PATH = sys.path[:]
|
||||||
try:
|
try:
|
||||||
_SIMPLE_JSON_PATH = os.path.join(_FILE_PATH,
|
_SIMPLE_JSON_PATH = os.path.join(_FILE_PATH, os.pardir, os.pardir,
|
||||||
os.pardir,
|
|
||||||
os.pardir,
|
|
||||||
'third_party')
|
'third_party')
|
||||||
sys.path.insert(0, _SIMPLE_JSON_PATH)
|
sys.path.insert(0, _SIMPLE_JSON_PATH)
|
||||||
# Add this path in case this is being used in the docs server.
|
# Add this path in case this is being used in the docs server.
|
||||||
sys.path.insert(0, os.path.join(_FILE_PATH,
|
sys.path.insert(
|
||||||
os.pardir,
|
0,
|
||||||
os.pardir,
|
os.path.join(_FILE_PATH, os.pardir, os.pardir, 'third_party',
|
||||||
'third_party',
|
'json_schema_compiler'))
|
||||||
'json_schema_compiler'))
|
|
||||||
import simplejson
|
import simplejson
|
||||||
from simplejson import OrderedDict
|
from simplejson import OrderedDict
|
||||||
finally:
|
finally:
|
||||||
|
@@ -17,8 +17,8 @@ def DeleteNodes(item, delete_key=None, matcher=None):
|
|||||||
|
|
||||||
def ShouldDelete(thing):
|
def ShouldDelete(thing):
|
||||||
return json_parse.IsDict(thing) and (
|
return json_parse.IsDict(thing) and (
|
||||||
delete_key is not None and delete_key in thing or
|
delete_key is not None and delete_key in thing
|
||||||
matcher is not None and matcher(thing))
|
or matcher is not None and matcher(thing))
|
||||||
|
|
||||||
if json_parse.IsDict(item):
|
if json_parse.IsDict(item):
|
||||||
toDelete = []
|
toDelete = []
|
||||||
@@ -30,8 +30,10 @@ def DeleteNodes(item, delete_key=None, matcher=None):
|
|||||||
for key in toDelete:
|
for key in toDelete:
|
||||||
del item[key]
|
del item[key]
|
||||||
elif type(item) == list:
|
elif type(item) == list:
|
||||||
item[:] = [DeleteNodes(thing, delete_key, matcher)
|
item[:] = [
|
||||||
for thing in item if not ShouldDelete(thing)]
|
DeleteNodes(thing, delete_key, matcher) for thing in item
|
||||||
|
if not ShouldDelete(thing)
|
||||||
|
]
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
@@ -6,93 +6,85 @@
|
|||||||
import json_schema
|
import json_schema
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class JsonSchemaUnittest(unittest.TestCase):
|
class JsonSchemaUnittest(unittest.TestCase):
|
||||||
|
|
||||||
def testNocompile(self):
|
def testNocompile(self):
|
||||||
compiled = [
|
compiled = [{
|
||||||
{
|
|
||||||
"namespace": "compile",
|
"namespace": "compile",
|
||||||
"description": "The compile API.",
|
"description": "The compile API.",
|
||||||
"functions": [],
|
"functions": [],
|
||||||
"types": {}
|
"types": {}
|
||||||
},
|
}, {
|
||||||
|
|
||||||
{
|
|
||||||
"namespace": "functions",
|
"namespace": "functions",
|
||||||
"description": "The functions API.",
|
"description": "The functions API.",
|
||||||
"functions": [
|
"functions": [{
|
||||||
{
|
|
||||||
"id": "two"
|
"id": "two"
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"id": "four"
|
"id": "four"
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
|
|
||||||
"types": {
|
"types": {
|
||||||
"one": { "key": "value" }
|
"one": {
|
||||||
|
"key": "value"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
|
|
||||||
{
|
|
||||||
"namespace": "types",
|
"namespace": "types",
|
||||||
"description": "The types API.",
|
"description": "The types API.",
|
||||||
"functions": [
|
"functions": [{
|
||||||
{ "id": "one" }
|
"id": "one"
|
||||||
],
|
}],
|
||||||
"types": {
|
"types": {
|
||||||
"two": {
|
"two": {
|
||||||
"key": "value"
|
"key": "value"
|
||||||
},
|
},
|
||||||
"four": {
|
"four": {
|
||||||
"key": "value"
|
"key": "value"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
|
|
||||||
{
|
|
||||||
"namespace": "nested",
|
"namespace": "nested",
|
||||||
"description": "The nested API.",
|
"description": "The nested API.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"sync": {
|
"sync": {
|
||||||
"functions": [
|
"functions": [{
|
||||||
{
|
"id": "two"
|
||||||
"id": "two"
|
}, {
|
||||||
},
|
"id": "four"
|
||||||
{
|
}],
|
||||||
"id": "four"
|
"types": {
|
||||||
}
|
"two": {
|
||||||
],
|
"key": "value"
|
||||||
"types": {
|
},
|
||||||
"two": {
|
"four": {
|
||||||
"key": "value"
|
"key": "value"
|
||||||
},
|
}
|
||||||
"four": {
|
}
|
||||||
"key": "value"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}]
|
||||||
]
|
|
||||||
|
|
||||||
schema = json_schema.CachedLoad('test/json_schema_test.json')
|
schema = json_schema.CachedLoad('test/json_schema_test.json')
|
||||||
self.assertEqual(compiled, json_schema.DeleteNodes(schema, 'nocompile'))
|
self.assertEqual(compiled, json_schema.DeleteNodes(schema, 'nocompile'))
|
||||||
|
|
||||||
def should_delete(value):
|
def should_delete(value):
|
||||||
return isinstance(value, dict) and not value.get('valid', True)
|
return isinstance(value, dict) and not value.get('valid', True)
|
||||||
expected = [
|
|
||||||
{'one': {'test': 'test'}},
|
expected = [{'one': {'test': 'test'}}, {'valid': True}, {}]
|
||||||
{'valid': True},
|
given = [{
|
||||||
{}
|
'one': {
|
||||||
]
|
'test': 'test'
|
||||||
given = [
|
},
|
||||||
{'one': {'test': 'test'}, 'two': {'valid': False}},
|
'two': {
|
||||||
{'valid': True},
|
'valid': False
|
||||||
{},
|
}
|
||||||
{'valid': False}
|
}, {
|
||||||
]
|
'valid': True
|
||||||
self.assertEqual(
|
}, {}, {
|
||||||
expected, json_schema.DeleteNodes(given, matcher=should_delete))
|
'valid': False
|
||||||
|
}]
|
||||||
|
self.assertEqual(expected,
|
||||||
|
json_schema.DeleteNodes(given, matcher=should_delete))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -2,13 +2,16 @@
|
|||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
def memoize(fn):
|
def memoize(fn):
|
||||||
'''Decorates |fn| to memoize.
|
'''Decorates |fn| to memoize.
|
||||||
'''
|
'''
|
||||||
memory = {}
|
memory = {}
|
||||||
|
|
||||||
def impl(*args, **optargs):
|
def impl(*args, **optargs):
|
||||||
full_args = args + tuple(optargs.items())
|
full_args = args + tuple(optargs.items())
|
||||||
if full_args not in memory:
|
if full_args not in memory:
|
||||||
memory[full_args] = fn(*args, **optargs)
|
memory[full_args] = fn(*args, **optargs)
|
||||||
return memory[full_args]
|
return memory[full_args]
|
||||||
|
|
||||||
return impl
|
return impl
|
||||||
|
@@ -17,14 +17,16 @@ def _IsTypeFromManifestKeys(namespace, typename, fallback):
|
|||||||
|
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
class ParseException(Exception):
|
class ParseException(Exception):
|
||||||
"""Thrown when data in the model is invalid.
|
"""Thrown when data in the model is invalid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent, message):
|
def __init__(self, parent, message):
|
||||||
hierarchy = _GetModelHierarchy(parent)
|
hierarchy = _GetModelHierarchy(parent)
|
||||||
hierarchy.append(message)
|
hierarchy.append(message)
|
||||||
Exception.__init__(
|
Exception.__init__(self,
|
||||||
self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
|
'Model parse exception at:\n' + '\n'.join(hierarchy))
|
||||||
|
|
||||||
|
|
||||||
class Model(object):
|
class Model(object):
|
||||||
@@ -33,6 +35,7 @@ class Model(object):
|
|||||||
Properties:
|
Properties:
|
||||||
- |namespaces| a map of a namespace name to its model.Namespace
|
- |namespaces| a map of a namespace name to its model.Namespace
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, allow_inline_enums=True):
|
def __init__(self, allow_inline_enums=True):
|
||||||
self._allow_inline_enums = allow_inline_enums
|
self._allow_inline_enums = allow_inline_enums
|
||||||
self.namespaces = {}
|
self.namespaces = {}
|
||||||
@@ -67,11 +70,13 @@ class ComplexFeature(object):
|
|||||||
- |unix_name| the unix_name of the feature
|
- |unix_name| the unix_name of the feature
|
||||||
- |feature_list| a list of simple features which make up the feature
|
- |feature_list| a list of simple features which make up the feature
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, feature_name, features):
|
def __init__(self, feature_name, features):
|
||||||
self.name = feature_name
|
self.name = feature_name
|
||||||
self.unix_name = UnixName(self.name)
|
self.unix_name = UnixName(self.name)
|
||||||
self.feature_list = features
|
self.feature_list = features
|
||||||
|
|
||||||
|
|
||||||
class SimpleFeature(object):
|
class SimpleFeature(object):
|
||||||
"""A simple feature, which can make up a complex feature, as specified in
|
"""A simple feature, which can make up a complex feature, as specified in
|
||||||
files such as chrome/common/extensions/api/_permission_features.json.
|
files such as chrome/common/extensions/api/_permission_features.json.
|
||||||
@@ -83,6 +88,7 @@ class SimpleFeature(object):
|
|||||||
- |extension_types| the types which can use the feature
|
- |extension_types| the types which can use the feature
|
||||||
- |allowlist| a list of extensions allowed to use the feature
|
- |allowlist| a list of extensions allowed to use the feature
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, feature_name, feature_def):
|
def __init__(self, feature_name, feature_def):
|
||||||
self.name = feature_name
|
self.name = feature_name
|
||||||
self.unix_name = UnixName(self.name)
|
self.unix_name = UnixName(self.name)
|
||||||
@@ -112,6 +118,7 @@ class Namespace(object):
|
|||||||
|include_compiler_options| is True
|
|include_compiler_options| is True
|
||||||
- |manifest_keys| is a Type representing the manifest keys for this namespace.
|
- |manifest_keys| is a Type representing the manifest keys for this namespace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
json,
|
json,
|
||||||
source_file,
|
source_file,
|
||||||
@@ -122,7 +129,7 @@ class Namespace(object):
|
|||||||
if 'description' not in json:
|
if 'description' not in json:
|
||||||
# TODO(kalman): Go back to throwing an error here.
|
# TODO(kalman): Go back to throwing an error here.
|
||||||
print('%s must have a "description" field. This will appear '
|
print('%s must have a "description" field. This will appear '
|
||||||
'on the API summary page.' % self.name)
|
'on the API summary page.' % self.name)
|
||||||
json['description'] = ''
|
json['description'] = ''
|
||||||
self.description = json['description']
|
self.description = json['description']
|
||||||
self.nodoc = json.get('nodoc', False)
|
self.nodoc = json.get('nodoc', False)
|
||||||
@@ -198,12 +205,8 @@ class Type(object):
|
|||||||
- |additional_properties| the type of the additional properties, if any is
|
- |additional_properties| the type of the additional properties, if any is
|
||||||
specified
|
specified
|
||||||
"""
|
"""
|
||||||
def __init__(self,
|
|
||||||
parent,
|
def __init__(self, parent, name, json, namespace, input_origin):
|
||||||
name,
|
|
||||||
json,
|
|
||||||
namespace,
|
|
||||||
input_origin):
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
# The typename "ManifestKeys" is reserved.
|
# The typename "ManifestKeys" is reserved.
|
||||||
@@ -221,8 +224,9 @@ class Type(object):
|
|||||||
# We need to do this to ensure types reference by manifest types have the
|
# We need to do this to ensure types reference by manifest types have the
|
||||||
# correct value for |origin.from_manifest_keys|.
|
# correct value for |origin.from_manifest_keys|.
|
||||||
self.origin = Origin(
|
self.origin = Origin(
|
||||||
input_origin.from_client, input_origin.from_json,
|
input_origin.from_client, input_origin.from_json,
|
||||||
_IsTypeFromManifestKeys(namespace, name, input_origin.from_manifest_keys))
|
_IsTypeFromManifestKeys(namespace, name,
|
||||||
|
input_origin.from_manifest_keys))
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.instance_of = json.get('isInstanceOf', None)
|
self.instance_of = json.get('isInstanceOf', None)
|
||||||
@@ -252,7 +256,7 @@ class Type(object):
|
|||||||
raise ParseException(
|
raise ParseException(
|
||||||
self,
|
self,
|
||||||
'Inline enum "%s" found in namespace "%s". These are not allowed. '
|
'Inline enum "%s" found in namespace "%s". These are not allowed. '
|
||||||
'See crbug.com/472279' % (name, namespace.name))
|
'See crbug.com/472279' % (name, namespace.name))
|
||||||
self.property_type = PropertyType.ENUM
|
self.property_type = PropertyType.ENUM
|
||||||
self.enum_values = [EnumValue(value, namespace) for value in json['enum']]
|
self.enum_values = [EnumValue(value, namespace) for value in json['enum']]
|
||||||
self.cpp_enum_prefix_override = json.get('cpp_enum_prefix_override', None)
|
self.cpp_enum_prefix_override = json.get('cpp_enum_prefix_override', None)
|
||||||
@@ -264,13 +268,13 @@ class Type(object):
|
|||||||
self.property_type = PropertyType.BOOLEAN
|
self.property_type = PropertyType.BOOLEAN
|
||||||
elif json_type == 'integer':
|
elif json_type == 'integer':
|
||||||
self.property_type = PropertyType.INTEGER
|
self.property_type = PropertyType.INTEGER
|
||||||
elif (json_type == 'double' or
|
elif (json_type == 'double' or json_type == 'number'):
|
||||||
json_type == 'number'):
|
|
||||||
self.property_type = PropertyType.DOUBLE
|
self.property_type = PropertyType.DOUBLE
|
||||||
elif json_type == 'string':
|
elif json_type == 'string':
|
||||||
self.property_type = PropertyType.STRING
|
self.property_type = PropertyType.STRING
|
||||||
elif 'choices' in json:
|
elif 'choices' in json:
|
||||||
self.property_type = PropertyType.CHOICES
|
self.property_type = PropertyType.CHOICES
|
||||||
|
|
||||||
def generate_type_name(type_json):
|
def generate_type_name(type_json):
|
||||||
if 'items' in type_json:
|
if 'items' in type_json:
|
||||||
return '%ss' % generate_type_name(type_json['items'])
|
return '%ss' % generate_type_name(type_json['items'])
|
||||||
@@ -279,28 +283,22 @@ class Type(object):
|
|||||||
if 'type' in type_json:
|
if 'type' in type_json:
|
||||||
return type_json['type']
|
return type_json['type']
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.choices = [
|
self.choices = [
|
||||||
Type(self,
|
Type(self,
|
||||||
generate_type_name(choice) or 'choice%s' % i,
|
generate_type_name(choice) or 'choice%s' % i, choice, namespace,
|
||||||
choice,
|
self.origin) for i, choice in enumerate(json['choices'])
|
||||||
namespace,
|
]
|
||||||
self.origin)
|
|
||||||
for i, choice in enumerate(json['choices'])]
|
|
||||||
elif json_type == 'object':
|
elif json_type == 'object':
|
||||||
if not (
|
if not ('isInstanceOf' in json or 'properties' in json
|
||||||
'isInstanceOf' in json or
|
or 'additionalProperties' in json or 'functions' in json
|
||||||
'properties' in json or
|
or 'events' in json):
|
||||||
'additionalProperties' in json or
|
|
||||||
'functions' in json or
|
|
||||||
'events' in json):
|
|
||||||
raise ParseException(self, name + " has no properties or functions")
|
raise ParseException(self, name + " has no properties or functions")
|
||||||
self.property_type = PropertyType.OBJECT
|
self.property_type = PropertyType.OBJECT
|
||||||
additional_properties_json = json.get('additionalProperties', None)
|
additional_properties_json = json.get('additionalProperties', None)
|
||||||
if additional_properties_json is not None:
|
if additional_properties_json is not None:
|
||||||
self.additional_properties = Type(self,
|
self.additional_properties = Type(self, 'additionalProperties',
|
||||||
'additionalProperties',
|
additional_properties_json, namespace,
|
||||||
additional_properties_json,
|
|
||||||
namespace,
|
|
||||||
self.origin)
|
self.origin)
|
||||||
else:
|
else:
|
||||||
self.additional_properties = None
|
self.additional_properties = None
|
||||||
@@ -309,8 +307,8 @@ class Type(object):
|
|||||||
# Sometimes we might have an unnamed function, e.g. if it's a property
|
# Sometimes we might have an unnamed function, e.g. if it's a property
|
||||||
# of an object. Use the name of the property in that case.
|
# of an object. Use the name of the property in that case.
|
||||||
function_name = json.get('name', name)
|
function_name = json.get('name', name)
|
||||||
self.function = Function(
|
self.function = Function(self, function_name, json, namespace,
|
||||||
self, function_name, json, namespace, self.origin)
|
self.origin)
|
||||||
else:
|
else:
|
||||||
raise ParseException(self, 'Unsupported JSON type %s' % json_type)
|
raise ParseException(self, 'Unsupported JSON type %s' % json_type)
|
||||||
|
|
||||||
@@ -321,6 +319,7 @@ class Type(object):
|
|||||||
'''
|
'''
|
||||||
return self.name == 'ManifestKeys'
|
return self.name == 'ManifestKeys'
|
||||||
|
|
||||||
|
|
||||||
class Function(object):
|
class Function(object):
|
||||||
"""A Function defined in the API.
|
"""A Function defined in the API.
|
||||||
|
|
||||||
@@ -340,12 +339,8 @@ class Function(object):
|
|||||||
- |returns| the return type of the function; None if the function does not
|
- |returns| the return type of the function; None if the function does not
|
||||||
return a value
|
return a value
|
||||||
"""
|
"""
|
||||||
def __init__(self,
|
|
||||||
parent,
|
def __init__(self, parent, name, json, namespace, origin):
|
||||||
name,
|
|
||||||
json,
|
|
||||||
namespace,
|
|
||||||
origin):
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.simple_name = _StripNamespace(self.name, namespace)
|
self.simple_name = _StripNamespace(self.name, namespace)
|
||||||
self.platforms = _GetPlatforms(json)
|
self.platforms = _GetPlatforms(json)
|
||||||
@@ -367,8 +362,10 @@ class Function(object):
|
|||||||
def GeneratePropertyFromParam(p):
|
def GeneratePropertyFromParam(p):
|
||||||
return Property(self, p['name'], p, namespace, origin)
|
return Property(self, p['name'], p, namespace, origin)
|
||||||
|
|
||||||
self.filters = [GeneratePropertyFromParam(filter_instance)
|
self.filters = [
|
||||||
for filter_instance in json.get('filters', [])]
|
GeneratePropertyFromParam(filter_instance)
|
||||||
|
for filter_instance in json.get('filters', [])
|
||||||
|
]
|
||||||
|
|
||||||
# Any asynchronous return should be defined using the returns_async field.
|
# Any asynchronous return should be defined using the returns_async field.
|
||||||
returns_async = json.get('returns_async', None)
|
returns_async = json.get('returns_async', None)
|
||||||
@@ -383,14 +380,11 @@ class Function(object):
|
|||||||
# incompatible with returning a promise. There are APIs that specify this,
|
# incompatible with returning a promise. There are APIs that specify this,
|
||||||
# though, so we make sure they have specified does_not_support_promises if
|
# though, so we make sure they have specified does_not_support_promises if
|
||||||
# they do.
|
# they do.
|
||||||
if (
|
if (json.get('returns') is not None
|
||||||
json.get('returns') is not None
|
and self.returns_async.can_return_promise):
|
||||||
and self.returns_async.can_return_promise
|
|
||||||
):
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Cannot specify both returns and returns_async on a function '
|
'Cannot specify both returns and returns_async on a function '
|
||||||
'which supports promies: %s.%s' % (namespace.name, name)
|
'which supports promies: %s.%s' % (namespace.name, name))
|
||||||
)
|
|
||||||
|
|
||||||
params = json.get('parameters', [])
|
params = json.get('parameters', [])
|
||||||
for i, param in enumerate(params):
|
for i, param in enumerate(params):
|
||||||
@@ -398,11 +392,8 @@ class Function(object):
|
|||||||
|
|
||||||
self.returns = None
|
self.returns = None
|
||||||
if 'returns' in json:
|
if 'returns' in json:
|
||||||
self.returns = Type(self,
|
self.returns = Type(self, '%sReturnType' % name, json['returns'],
|
||||||
'%sReturnType' % name,
|
namespace, origin)
|
||||||
json['returns'],
|
|
||||||
namespace,
|
|
||||||
origin)
|
|
||||||
|
|
||||||
|
|
||||||
class ReturnsAsync(object):
|
class ReturnsAsync(object):
|
||||||
@@ -423,6 +414,7 @@ class ReturnsAsync(object):
|
|||||||
callback. Currently only consumed for documentation
|
callback. Currently only consumed for documentation
|
||||||
purposes
|
purposes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent, json, namespace, origin):
|
def __init__(self, parent, json, namespace, origin):
|
||||||
self.name = json.get('name')
|
self.name = json.get('name')
|
||||||
self.simple_name = _StripNamespace(self.name, namespace)
|
self.simple_name = _StripNamespace(self.name, namespace)
|
||||||
@@ -434,25 +426,21 @@ class ReturnsAsync(object):
|
|||||||
|
|
||||||
if json.get('returns') is not None:
|
if json.get('returns') is not None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Cannot return a value from an asynchronous return: %s.%s in %s'
|
'Cannot return a value from an asynchronous return: %s.%s in %s' %
|
||||||
% (namespace.name, parent.name, namespace.source_file)
|
(namespace.name, parent.name, namespace.source_file))
|
||||||
)
|
|
||||||
if json.get('deprecated') is not None:
|
if json.get('deprecated') is not None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Cannot specify deprecated on an asynchronous return: %s.%s in %s'
|
'Cannot specify deprecated on an asynchronous return: %s.%s in %s' %
|
||||||
% (namespace.name, parent.name, namespace.source_file)
|
(namespace.name, parent.name, namespace.source_file))
|
||||||
)
|
|
||||||
if json.get('parameters') is None:
|
if json.get('parameters') is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'parameters key not specified on returns_async: %s.%s in %s'
|
'parameters key not specified on returns_async: %s.%s in %s' %
|
||||||
% (namespace.name, parent.name, namespace.source_file)
|
(namespace.name, parent.name, namespace.source_file))
|
||||||
)
|
|
||||||
if len(json.get('parameters')) > 1 and self.can_return_promise:
|
if len(json.get('parameters')) > 1 and self.can_return_promise:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Only a single parameter can be specific on a returns_async which'
|
'Only a single parameter can be specific on a returns_async which'
|
||||||
' supports promises: %s.%s in %s'
|
' supports promises: %s.%s in %s' %
|
||||||
% (namespace.name, parent.name, namespace.source_file)
|
(namespace.name, parent.name, namespace.source_file))
|
||||||
)
|
|
||||||
|
|
||||||
def GeneratePropertyFromParam(p):
|
def GeneratePropertyFromParam(p):
|
||||||
return Property(self, p['name'], p, namespace, origin)
|
return Property(self, p['name'], p, namespace, origin)
|
||||||
@@ -475,6 +463,7 @@ class Property(object):
|
|||||||
- |simple_name| the name of this Property without a namespace
|
- |simple_name| the name of this Property without a namespace
|
||||||
- |deprecated| a reason and possible alternative for a deprecated property
|
- |deprecated| a reason and possible alternative for a deprecated property
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent, name, json, namespace, origin):
|
def __init__(self, parent, name, json, namespace, origin):
|
||||||
"""Creates a Property from JSON.
|
"""Creates a Property from JSON.
|
||||||
"""
|
"""
|
||||||
@@ -491,11 +480,10 @@ class Property(object):
|
|||||||
self.nodoc = json.get('nodoc', False)
|
self.nodoc = json.get('nodoc', False)
|
||||||
|
|
||||||
# HACK: only support very specific value types.
|
# HACK: only support very specific value types.
|
||||||
is_allowed_value = (
|
is_allowed_value = ('$ref' not in json
|
||||||
'$ref' not in json and
|
and ('type' not in json or json['type'] == 'integer'
|
||||||
('type' not in json or json['type'] == 'integer'
|
or json['type'] == 'number'
|
||||||
or json['type'] == 'number'
|
or json['type'] == 'string'))
|
||||||
or json['type'] == 'string'))
|
|
||||||
|
|
||||||
self.value = None
|
self.value = None
|
||||||
if 'value' in json and is_allowed_value:
|
if 'value' in json and is_allowed_value:
|
||||||
@@ -532,20 +520,21 @@ class Property(object):
|
|||||||
if unix_name == self._unix_name:
|
if unix_name == self._unix_name:
|
||||||
return
|
return
|
||||||
if self._unix_name_used:
|
if self._unix_name_used:
|
||||||
raise AttributeError(
|
raise AttributeError('Cannot set the unix_name on %s; '
|
||||||
'Cannot set the unix_name on %s; '
|
'it is already used elsewhere as %s' %
|
||||||
'it is already used elsewhere as %s' %
|
(self.name, self._unix_name))
|
||||||
(self.name, self._unix_name))
|
|
||||||
self._unix_name = unix_name
|
self._unix_name = unix_name
|
||||||
|
|
||||||
unix_name = property(GetUnixName, SetUnixName)
|
unix_name = property(GetUnixName, SetUnixName)
|
||||||
|
|
||||||
|
|
||||||
class EnumValue(object):
|
class EnumValue(object):
|
||||||
"""A single value from an enum.
|
"""A single value from an enum.
|
||||||
Properties:
|
Properties:
|
||||||
- |name| name of the property as in the json.
|
- |name| name of the property as in the json.
|
||||||
- |description| a description of the property (if provided)
|
- |description| a description of the property (if provided)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, json, namespace):
|
def __init__(self, json, namespace):
|
||||||
if isinstance(json, dict):
|
if isinstance(json, dict):
|
||||||
self.name = json['name']
|
self.name = json['name']
|
||||||
@@ -556,17 +545,18 @@ class EnumValue(object):
|
|||||||
|
|
||||||
# Using empty string values as enum key is only allowed in a few namespaces,
|
# Using empty string values as enum key is only allowed in a few namespaces,
|
||||||
# as an exception to the rule, and we should not add more.
|
# as an exception to the rule, and we should not add more.
|
||||||
if (not self.name and
|
if (not self.name and namespace.name not in ['enums', 'webstorePrivate']):
|
||||||
namespace.name not in ['enums', 'webstorePrivate']):
|
|
||||||
raise ValueError('Enum value cannot be an empty string')
|
raise ValueError('Enum value cannot be an empty string')
|
||||||
|
|
||||||
def CamelName(self):
|
def CamelName(self):
|
||||||
return CamelName(self.name)
|
return CamelName(self.name)
|
||||||
|
|
||||||
|
|
||||||
class _Enum(object):
|
class _Enum(object):
|
||||||
"""Superclass for enum types with a "name" field, setting up repr/eq/ne.
|
"""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.
|
Enums need to do this so that equality/non-equality work over pickling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def GetAll(cls):
|
def GetAll(cls):
|
||||||
"""Yields all _Enum objects declared in |cls|.
|
"""Yields all _Enum objects declared in |cls|.
|
||||||
@@ -581,6 +571,7 @@ class _Enum(object):
|
|||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return type(other) == type(self) and other.name == self.name
|
return type(other) == type(self) and other.name == self.name
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not (self == other)
|
return not (self == other)
|
||||||
|
|
||||||
@@ -595,6 +586,7 @@ class _Enum(object):
|
|||||||
|
|
||||||
|
|
||||||
class _PropertyTypeInfo(_Enum):
|
class _PropertyTypeInfo(_Enum):
|
||||||
|
|
||||||
def __init__(self, is_fundamental, name):
|
def __init__(self, is_fundamental, name):
|
||||||
_Enum.__init__(self, name)
|
_Enum.__init__(self, name)
|
||||||
self.is_fundamental = is_fundamental
|
self.is_fundamental = is_fundamental
|
||||||
@@ -602,6 +594,7 @@ class _PropertyTypeInfo(_Enum):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class PropertyType(object):
|
class PropertyType(object):
|
||||||
"""Enum of different types of properties/parameters.
|
"""Enum of different types of properties/parameters.
|
||||||
"""
|
"""
|
||||||
@@ -619,111 +612,113 @@ class PropertyType(object):
|
|||||||
REF = _PropertyTypeInfo(False, "ref")
|
REF = _PropertyTypeInfo(False, "ref")
|
||||||
STRING = _PropertyTypeInfo(True, "string")
|
STRING = _PropertyTypeInfo(True, "string")
|
||||||
|
|
||||||
|
|
||||||
def IsCPlusPlusKeyword(name):
|
def IsCPlusPlusKeyword(name):
|
||||||
"""Returns true if `name` is a C++ reserved keyword.
|
"""Returns true if `name` is a C++ reserved keyword.
|
||||||
"""
|
"""
|
||||||
# Obtained from https://en.cppreference.com/w/cpp/keyword.
|
# Obtained from https://en.cppreference.com/w/cpp/keyword.
|
||||||
keywords = {
|
keywords = {
|
||||||
"alignas",
|
"alignas",
|
||||||
"alignof",
|
"alignof",
|
||||||
"and",
|
"and",
|
||||||
"and_eq",
|
"and_eq",
|
||||||
"asm",
|
"asm",
|
||||||
"atomic_cancel",
|
"atomic_cancel",
|
||||||
"atomic_commit",
|
"atomic_commit",
|
||||||
"atomic_noexcept",
|
"atomic_noexcept",
|
||||||
"auto",
|
"auto",
|
||||||
"bitand",
|
"bitand",
|
||||||
"bitor",
|
"bitor",
|
||||||
"bool",
|
"bool",
|
||||||
"break",
|
"break",
|
||||||
"case",
|
"case",
|
||||||
"catch",
|
"catch",
|
||||||
"char",
|
"char",
|
||||||
"char8_t",
|
"char8_t",
|
||||||
"char16_t",
|
"char16_t",
|
||||||
"char32_t",
|
"char32_t",
|
||||||
"class",
|
"class",
|
||||||
"compl",
|
"compl",
|
||||||
"concept",
|
"concept",
|
||||||
"const",
|
"const",
|
||||||
"consteval",
|
"consteval",
|
||||||
"constexpr",
|
"constexpr",
|
||||||
"constinit",
|
"constinit",
|
||||||
"const_cast",
|
"const_cast",
|
||||||
"continue",
|
"continue",
|
||||||
"co_await",
|
"co_await",
|
||||||
"co_return",
|
"co_return",
|
||||||
"co_yield",
|
"co_yield",
|
||||||
"decltype",
|
"decltype",
|
||||||
"default",
|
"default",
|
||||||
"delete",
|
"delete",
|
||||||
"do",
|
"do",
|
||||||
"double",
|
"double",
|
||||||
"dynamic_cast",
|
"dynamic_cast",
|
||||||
"else",
|
"else",
|
||||||
"enum",
|
"enum",
|
||||||
"explicit",
|
"explicit",
|
||||||
"export",
|
"export",
|
||||||
"extern",
|
"extern",
|
||||||
"false",
|
"false",
|
||||||
"float",
|
"float",
|
||||||
"for",
|
"for",
|
||||||
"friend",
|
"friend",
|
||||||
"goto",
|
"goto",
|
||||||
"if",
|
"if",
|
||||||
"inline",
|
"inline",
|
||||||
"int",
|
"int",
|
||||||
"long",
|
"long",
|
||||||
"mutable",
|
"mutable",
|
||||||
"namespace",
|
"namespace",
|
||||||
"new",
|
"new",
|
||||||
"noexcept",
|
"noexcept",
|
||||||
"not",
|
"not",
|
||||||
"not_eq",
|
"not_eq",
|
||||||
"nullptr",
|
"nullptr",
|
||||||
"operator",
|
"operator",
|
||||||
"or",
|
"or",
|
||||||
"or_eq",
|
"or_eq",
|
||||||
"private",
|
"private",
|
||||||
"protected",
|
"protected",
|
||||||
"public",
|
"public",
|
||||||
"reflexpr",
|
"reflexpr",
|
||||||
"register",
|
"register",
|
||||||
"reinterpret_cast",
|
"reinterpret_cast",
|
||||||
"requires",
|
"requires",
|
||||||
"return",
|
"return",
|
||||||
"short",
|
"short",
|
||||||
"signed",
|
"signed",
|
||||||
"sizeof",
|
"sizeof",
|
||||||
"static",
|
"static",
|
||||||
"static_assert",
|
"static_assert",
|
||||||
"static_cast",
|
"static_cast",
|
||||||
"struct",
|
"struct",
|
||||||
"switch",
|
"switch",
|
||||||
"synchronized",
|
"synchronized",
|
||||||
"template",
|
"template",
|
||||||
"this",
|
"this",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"throw",
|
"throw",
|
||||||
"true",
|
"true",
|
||||||
"try",
|
"try",
|
||||||
"typedef",
|
"typedef",
|
||||||
"typeid",
|
"typeid",
|
||||||
"typename",
|
"typename",
|
||||||
"union",
|
"union",
|
||||||
"unsigned",
|
"unsigned",
|
||||||
"using",
|
"using",
|
||||||
"virtual",
|
"virtual",
|
||||||
"void",
|
"void",
|
||||||
"volatile",
|
"volatile",
|
||||||
"wchar_t",
|
"wchar_t",
|
||||||
"while",
|
"while",
|
||||||
"xor",
|
"xor",
|
||||||
"xor_eq"
|
"xor_eq",
|
||||||
}
|
}
|
||||||
return name in keywords
|
return name in keywords
|
||||||
|
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
def UnixName(name):
|
def UnixName(name):
|
||||||
'''Returns the unix_style name for a given lowerCamelCase string.
|
'''Returns the unix_style name for a given lowerCamelCase string.
|
||||||
@@ -740,7 +735,7 @@ def UnixName(name):
|
|||||||
# Prepend an extra underscore to the |name|'s start if it doesn't start with a
|
# Prepend an extra underscore to the |name|'s start if it doesn't start with a
|
||||||
# letter or underscore to ensure the generated unix name follows C++
|
# letter or underscore to ensure the generated unix name follows C++
|
||||||
# identifier rules.
|
# identifier rules.
|
||||||
assert(name)
|
assert (name)
|
||||||
if name[0].isdigit():
|
if name[0].isdigit():
|
||||||
name = '_' + name
|
name = '_' + name
|
||||||
|
|
||||||
@@ -811,10 +806,7 @@ def _GetFunctions(parent, json, namespace):
|
|||||||
"""
|
"""
|
||||||
functions = OrderedDict()
|
functions = OrderedDict()
|
||||||
for function_json in json.get('functions', []):
|
for function_json in json.get('functions', []):
|
||||||
function = Function(parent,
|
function = Function(parent, function_json['name'], function_json, namespace,
|
||||||
function_json['name'],
|
|
||||||
function_json,
|
|
||||||
namespace,
|
|
||||||
Origin(from_json=True))
|
Origin(from_json=True))
|
||||||
functions[function.name] = function
|
functions[function.name] = function
|
||||||
return functions
|
return functions
|
||||||
@@ -825,10 +817,7 @@ def _GetEvents(parent, json, namespace):
|
|||||||
"""
|
"""
|
||||||
events = OrderedDict()
|
events = OrderedDict()
|
||||||
for event_json in json.get('events', []):
|
for event_json in json.get('events', []):
|
||||||
event = Function(parent,
|
event = Function(parent, event_json['name'], event_json, namespace,
|
||||||
event_json['name'],
|
|
||||||
event_json,
|
|
||||||
namespace,
|
|
||||||
Origin(from_client=True))
|
Origin(from_client=True))
|
||||||
events[event.name] = event
|
events[event.name] = event
|
||||||
return events
|
return events
|
||||||
@@ -853,12 +842,13 @@ def _GetManifestKeysType(self, json):
|
|||||||
|
|
||||||
# Create a dummy object to parse "manifest_keys" as a type.
|
# Create a dummy object to parse "manifest_keys" as a type.
|
||||||
manifest_keys_type = {
|
manifest_keys_type = {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': json['manifest_keys'],
|
'properties': json['manifest_keys'],
|
||||||
}
|
}
|
||||||
return Type(self, 'ManifestKeys', manifest_keys_type, self,
|
return Type(self, 'ManifestKeys', manifest_keys_type, self,
|
||||||
Origin(from_manifest_keys=True))
|
Origin(from_manifest_keys=True))
|
||||||
|
|
||||||
|
|
||||||
def _GetWithDefaultChecked(self, json, key, default):
|
def _GetWithDefaultChecked(self, json, key, default):
|
||||||
if json.get(key) == default:
|
if json.get(key) == default:
|
||||||
raise ParseException(
|
raise ParseException(
|
||||||
@@ -867,7 +857,9 @@ def _GetWithDefaultChecked(self, json, key, default):
|
|||||||
% (key, default))
|
% (key, default))
|
||||||
return json.get(key, default)
|
return json.get(key, default)
|
||||||
|
|
||||||
|
|
||||||
class _PlatformInfo(_Enum):
|
class _PlatformInfo(_Enum):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
_Enum.__init__(self, name)
|
_Enum.__init__(self, name)
|
||||||
|
|
||||||
|
@@ -9,63 +9,62 @@ from model import Platforms
|
|||||||
import model
|
import model
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class ModelTest(unittest.TestCase):
|
class ModelTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.model = model.Model()
|
self.model = model.Model()
|
||||||
self.permissions_json = CachedLoad('test/permissions.json')
|
self.permissions_json = CachedLoad('test/permissions.json')
|
||||||
self.model.AddNamespace(self.permissions_json[0],
|
self.model.AddNamespace(self.permissions_json[0],
|
||||||
'path/to/permissions.json')
|
'path/to/permissions.json')
|
||||||
self.permissions = self.model.namespaces.get('permissions')
|
self.permissions = self.model.namespaces.get('permissions')
|
||||||
self.windows_json = CachedLoad('test/windows.json')
|
self.windows_json = CachedLoad('test/windows.json')
|
||||||
self.model.AddNamespace(self.windows_json[0],
|
self.model.AddNamespace(self.windows_json[0], 'path/to/window.json')
|
||||||
'path/to/window.json')
|
|
||||||
self.windows = self.model.namespaces.get('windows')
|
self.windows = self.model.namespaces.get('windows')
|
||||||
self.tabs_json = CachedLoad('test/tabs.json')
|
self.tabs_json = CachedLoad('test/tabs.json')
|
||||||
self.model.AddNamespace(self.tabs_json[0],
|
self.model.AddNamespace(self.tabs_json[0], 'path/to/tabs.json')
|
||||||
'path/to/tabs.json')
|
|
||||||
self.tabs = self.model.namespaces.get('tabs')
|
self.tabs = self.model.namespaces.get('tabs')
|
||||||
self.idl_chromeos = Load('test/idl_namespace_chromeos.idl')
|
self.idl_chromeos = Load('test/idl_namespace_chromeos.idl')
|
||||||
self.model.AddNamespace(self.idl_chromeos[0],
|
self.model.AddNamespace(self.idl_chromeos[0],
|
||||||
'path/to/idl_namespace_chromeos.idl')
|
'path/to/idl_namespace_chromeos.idl')
|
||||||
self.idl_namespace_chromeos = self.model.namespaces.get(
|
self.idl_namespace_chromeos = self.model.namespaces.get(
|
||||||
'idl_namespace_chromeos')
|
'idl_namespace_chromeos')
|
||||||
self.idl_all_platforms = Load('test/idl_namespace_all_platforms.idl')
|
self.idl_all_platforms = Load('test/idl_namespace_all_platforms.idl')
|
||||||
self.model.AddNamespace(self.idl_all_platforms[0],
|
self.model.AddNamespace(self.idl_all_platforms[0],
|
||||||
'path/to/idl_namespace_all_platforms.idl')
|
'path/to/idl_namespace_all_platforms.idl')
|
||||||
self.idl_namespace_all_platforms = self.model.namespaces.get(
|
self.idl_namespace_all_platforms = self.model.namespaces.get(
|
||||||
'idl_namespace_all_platforms')
|
'idl_namespace_all_platforms')
|
||||||
self.idl_non_specific_platforms = Load(
|
self.idl_non_specific_platforms = Load(
|
||||||
'test/idl_namespace_non_specific_platforms.idl')
|
'test/idl_namespace_non_specific_platforms.idl')
|
||||||
self.model.AddNamespace(self.idl_non_specific_platforms[0],
|
self.model.AddNamespace(self.idl_non_specific_platforms[0],
|
||||||
'path/to/idl_namespace_non_specific_platforms.idl')
|
'path/to/idl_namespace_non_specific_platforms.idl')
|
||||||
self.idl_namespace_non_specific_platforms = self.model.namespaces.get(
|
self.idl_namespace_non_specific_platforms = self.model.namespaces.get(
|
||||||
'idl_namespace_non_specific_platforms')
|
'idl_namespace_non_specific_platforms')
|
||||||
self.returns_async_json = CachedLoad('test/returns_async.json')
|
self.returns_async_json = CachedLoad('test/returns_async.json')
|
||||||
self.model.AddNamespace(self.returns_async_json[0],
|
self.model.AddNamespace(self.returns_async_json[0],
|
||||||
'path/to/returns_async.json')
|
'path/to/returns_async.json')
|
||||||
self.returns_async = self.model.namespaces.get('returns_async')
|
self.returns_async = self.model.namespaces.get('returns_async')
|
||||||
self.idl_returns_async_idl = Load('test/idl_returns_async.idl')
|
self.idl_returns_async_idl = Load('test/idl_returns_async.idl')
|
||||||
self.model.AddNamespace(self.idl_returns_async_idl[0],
|
self.model.AddNamespace(self.idl_returns_async_idl[0],
|
||||||
'path/to/idl_returns_async.idl')
|
'path/to/idl_returns_async.idl')
|
||||||
self.idl_returns_async = self.model.namespaces.get('idl_returns_async')
|
self.idl_returns_async = self.model.namespaces.get('idl_returns_async')
|
||||||
self.nodoc_json = CachedLoad('test/namespace_nodoc.json')
|
self.nodoc_json = CachedLoad('test/namespace_nodoc.json')
|
||||||
self.model.AddNamespace(self.nodoc_json[0],
|
self.model.AddNamespace(self.nodoc_json[0], 'path/to/namespace_nodoc.json')
|
||||||
'path/to/namespace_nodoc.json')
|
|
||||||
self.nodoc = self.model.namespaces.get('nodoc')
|
self.nodoc = self.model.namespaces.get('nodoc')
|
||||||
self.fakeapi_json = CachedLoad('test/namespace_fakeapi.json')
|
self.fakeapi_json = CachedLoad('test/namespace_fakeapi.json')
|
||||||
self.model.AddNamespace(self.fakeapi_json[0],
|
self.model.AddNamespace(self.fakeapi_json[0],
|
||||||
'path/to/namespace_fakeapi.json')
|
'path/to/namespace_fakeapi.json')
|
||||||
self.fakeapi = self.model.namespaces.get('fakeapi')
|
self.fakeapi = self.model.namespaces.get('fakeapi')
|
||||||
|
|
||||||
self.function_platforms_idl = Load('test/function_platforms.idl')
|
self.function_platforms_idl = Load('test/function_platforms.idl')
|
||||||
self.model.AddNamespace(self.function_platforms_idl[0],
|
self.model.AddNamespace(self.function_platforms_idl[0],
|
||||||
'/path/to/function_platforms.idl')
|
'/path/to/function_platforms.idl')
|
||||||
self.function_platforms = self.model.namespaces.get('function_platforms')
|
self.function_platforms = self.model.namespaces.get('function_platforms')
|
||||||
|
|
||||||
self.function_platform_win_linux_json = CachedLoad(
|
self.function_platform_win_linux_json = CachedLoad(
|
||||||
'test/function_platform_win_linux.json')
|
'test/function_platform_win_linux.json')
|
||||||
self.model.AddNamespace(self.function_platform_win_linux_json[0],
|
self.model.AddNamespace(self.function_platform_win_linux_json[0],
|
||||||
'path/to/function_platform_win_linux.json')
|
'path/to/function_platform_win_linux.json')
|
||||||
self.function_platform_win_linux = self.model.namespaces.get(
|
self.function_platform_win_linux = self.model.namespaces.get(
|
||||||
'function_platform_win_linux')
|
'function_platform_win_linux')
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ class ModelTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testHasFunctions(self):
|
def testHasFunctions(self):
|
||||||
self.assertEqual(["contains", "getAll", "remove", "request"],
|
self.assertEqual(["contains", "getAll", "remove", "request"],
|
||||||
sorted(self.permissions.functions.keys()))
|
sorted(self.permissions.functions.keys()))
|
||||||
|
|
||||||
def testHasTypes(self):
|
def testHasTypes(self):
|
||||||
self.assertEqual(['Tab'], list(self.tabs.types.keys()))
|
self.assertEqual(['Tab'], list(self.tabs.types.keys()))
|
||||||
@@ -83,41 +82,38 @@ class ModelTest(unittest.TestCase):
|
|||||||
self.assertEqual(['Window'], list(self.windows.types.keys()))
|
self.assertEqual(['Window'], list(self.windows.types.keys()))
|
||||||
|
|
||||||
def testHasProperties(self):
|
def testHasProperties(self):
|
||||||
self.assertEqual(["active", "favIconUrl", "highlighted", "id",
|
self.assertEqual([
|
||||||
"incognito", "index", "pinned", "selected", "status", "title", "url",
|
"active", "favIconUrl", "highlighted", "id", "incognito", "index",
|
||||||
"windowId"],
|
"pinned", "selected", "status", "title", "url", "windowId"
|
||||||
sorted(self.tabs.types['Tab'].properties.keys()))
|
], sorted(self.tabs.types['Tab'].properties.keys()))
|
||||||
|
|
||||||
def testProperties(self):
|
def testProperties(self):
|
||||||
string_prop = self.tabs.types['Tab'].properties['status']
|
string_prop = self.tabs.types['Tab'].properties['status']
|
||||||
self.assertEqual(model.PropertyType.STRING,
|
self.assertEqual(model.PropertyType.STRING, string_prop.type_.property_type)
|
||||||
string_prop.type_.property_type)
|
|
||||||
integer_prop = self.tabs.types['Tab'].properties['id']
|
integer_prop = self.tabs.types['Tab'].properties['id']
|
||||||
self.assertEqual(model.PropertyType.INTEGER,
|
self.assertEqual(model.PropertyType.INTEGER,
|
||||||
integer_prop.type_.property_type)
|
integer_prop.type_.property_type)
|
||||||
array_prop = self.windows.types['Window'].properties['tabs']
|
array_prop = self.windows.types['Window'].properties['tabs']
|
||||||
self.assertEqual(model.PropertyType.ARRAY,
|
self.assertEqual(model.PropertyType.ARRAY, array_prop.type_.property_type)
|
||||||
array_prop.type_.property_type)
|
|
||||||
self.assertEqual(model.PropertyType.REF,
|
self.assertEqual(model.PropertyType.REF,
|
||||||
array_prop.type_.item_type.property_type)
|
array_prop.type_.item_type.property_type)
|
||||||
self.assertEqual('tabs.Tab', array_prop.type_.item_type.ref_type)
|
self.assertEqual('tabs.Tab', array_prop.type_.item_type.ref_type)
|
||||||
object_prop = self.tabs.functions['query'].params[0]
|
object_prop = self.tabs.functions['query'].params[0]
|
||||||
self.assertEqual(model.PropertyType.OBJECT,
|
self.assertEqual(model.PropertyType.OBJECT, object_prop.type_.property_type)
|
||||||
object_prop.type_.property_type)
|
self.assertEqual([
|
||||||
self.assertEqual(
|
"active", "highlighted", "pinned", "status", "title", "url", "windowId",
|
||||||
["active", "highlighted", "pinned", "status", "title", "url",
|
"windowType"
|
||||||
"windowId", "windowType"],
|
], sorted(object_prop.type_.properties.keys()))
|
||||||
sorted(object_prop.type_.properties.keys()))
|
|
||||||
|
|
||||||
def testChoices(self):
|
def testChoices(self):
|
||||||
self.assertEqual(model.PropertyType.CHOICES,
|
self.assertEqual(model.PropertyType.CHOICES,
|
||||||
self.tabs.functions['move'].params[0].type_.property_type)
|
self.tabs.functions['move'].params[0].type_.property_type)
|
||||||
|
|
||||||
def testPropertyNotImplemented(self):
|
def testPropertyNotImplemented(self):
|
||||||
(self.permissions_json[0]['types'][0]
|
(self.permissions_json[0]['types'][0]['properties']['permissions']['type']
|
||||||
['properties']['permissions']['type']) = 'something'
|
) = 'something'
|
||||||
self.assertRaises(model.ParseException, self.model.AddNamespace,
|
self.assertRaises(model.ParseException, self.model.AddNamespace,
|
||||||
self.permissions_json[0], 'path/to/something.json')
|
self.permissions_json[0], 'path/to/something.json')
|
||||||
|
|
||||||
def testDefaultSpecifiedRedundantly(self):
|
def testDefaultSpecifiedRedundantly(self):
|
||||||
test_json = CachedLoad('test/redundant_default_attribute.json')
|
test_json = CachedLoad('test/redundant_default_attribute.json')
|
||||||
@@ -127,20 +123,16 @@ class ModelTest(unittest.TestCase):
|
|||||||
' in path/to/redundant_default_attribute.json\n'
|
' in path/to/redundant_default_attribute.json\n'
|
||||||
'The attribute "optional" is specified as "False", but this is the '
|
'The attribute "optional" is specified as "False", but this is the '
|
||||||
'default value if the attribute is not included\. It should be '
|
'default value if the attribute is not included\. It should be '
|
||||||
'removed\.',
|
'removed\.', self.model.AddNamespace, test_json[0],
|
||||||
self.model.AddNamespace,
|
|
||||||
test_json[0],
|
|
||||||
'path/to/redundant_default_attribute.json')
|
'path/to/redundant_default_attribute.json')
|
||||||
|
|
||||||
def testReturnsAsyncMissingParametersKey(self):
|
def testReturnsAsyncMissingParametersKey(self):
|
||||||
test_json = CachedLoad('test/returns_async_missing_parameters_key.json')
|
test_json = CachedLoad('test/returns_async_missing_parameters_key.json')
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
ValueError,
|
ValueError, 'parameters key not specified on returns_async: '
|
||||||
'parameters key not specified on returns_async: '
|
|
||||||
'returnsAsyncMissingParametersKey.asyncNoParametersKey in '
|
'returnsAsyncMissingParametersKey.asyncNoParametersKey in '
|
||||||
'path/to/returns_async_missing_parameters_key.json',
|
'path/to/returns_async_missing_parameters_key.json',
|
||||||
self.model.AddNamespace,
|
self.model.AddNamespace, test_json[0],
|
||||||
test_json[0],
|
|
||||||
'path/to/returns_async_missing_parameters_key.json')
|
'path/to/returns_async_missing_parameters_key.json')
|
||||||
|
|
||||||
def testDescription(self):
|
def testDescription(self):
|
||||||
@@ -168,45 +160,44 @@ class ModelTest(unittest.TestCase):
|
|||||||
|
|
||||||
def testUnixName(self):
|
def testUnixName(self):
|
||||||
expectations = {
|
expectations = {
|
||||||
'foo': 'foo',
|
'foo': 'foo',
|
||||||
'fooBar': 'foo_bar',
|
'fooBar': 'foo_bar',
|
||||||
'fooBarBaz': 'foo_bar_baz',
|
'fooBarBaz': 'foo_bar_baz',
|
||||||
'fooBARBaz': 'foo_bar_baz',
|
'fooBARBaz': 'foo_bar_baz',
|
||||||
'fooBAR': 'foo_bar',
|
'fooBAR': 'foo_bar',
|
||||||
'FOO': 'foo',
|
'FOO': 'foo',
|
||||||
'FOOBar': 'foo_bar',
|
'FOOBar': 'foo_bar',
|
||||||
'foo.bar': 'foo_bar',
|
'foo.bar': 'foo_bar',
|
||||||
'foo.BAR': 'foo_bar',
|
'foo.BAR': 'foo_bar',
|
||||||
'foo.barBAZ': 'foo_bar_baz',
|
'foo.barBAZ': 'foo_bar_baz',
|
||||||
'foo_Bar_Baz_box': 'foo_bar_baz_box',
|
'foo_Bar_Baz_box': 'foo_bar_baz_box',
|
||||||
}
|
}
|
||||||
for name in expectations:
|
for name in expectations:
|
||||||
self.assertEqual(expectations[name], model.UnixName(name))
|
self.assertEqual(expectations[name], model.UnixName(name))
|
||||||
|
|
||||||
def testCamelName(self):
|
def testCamelName(self):
|
||||||
expectations = {
|
expectations = {
|
||||||
'foo': 'foo',
|
'foo': 'foo',
|
||||||
'fooBar': 'fooBar',
|
'fooBar': 'fooBar',
|
||||||
'foo_bar_baz': 'fooBarBaz',
|
'foo_bar_baz': 'fooBarBaz',
|
||||||
'FOO_BAR': 'FOOBar',
|
'FOO_BAR': 'FOOBar',
|
||||||
'FOO_bar': 'FOOBar',
|
'FOO_bar': 'FOOBar',
|
||||||
'_bar': 'Bar',
|
'_bar': 'Bar',
|
||||||
'_bar_baz': 'BarBaz',
|
'_bar_baz': 'BarBaz',
|
||||||
'bar_': 'bar',
|
'bar_': 'bar',
|
||||||
'bar_baz_': 'barBaz',
|
'bar_baz_': 'barBaz',
|
||||||
}
|
}
|
||||||
for testcase, expected in expectations.items():
|
for testcase, expected in expectations.items():
|
||||||
self.assertEqual(expected, model.CamelName(testcase))
|
self.assertEqual(expected, model.CamelName(testcase))
|
||||||
|
|
||||||
def testPlatforms(self):
|
def testPlatforms(self):
|
||||||
self.assertEqual([Platforms.CHROMEOS],
|
self.assertEqual([Platforms.CHROMEOS],
|
||||||
self.idl_namespace_chromeos.platforms)
|
self.idl_namespace_chromeos.platforms)
|
||||||
self.assertEqual(
|
self.assertEqual([
|
||||||
[Platforms.CHROMEOS, Platforms.FUCHSIA, Platforms.LINUX, Platforms.MAC,
|
Platforms.CHROMEOS, Platforms.FUCHSIA, Platforms.LINUX, Platforms.MAC,
|
||||||
Platforms.WIN],
|
Platforms.WIN
|
||||||
self.idl_namespace_all_platforms.platforms)
|
], self.idl_namespace_all_platforms.platforms)
|
||||||
self.assertEqual(None,
|
self.assertEqual(None, self.idl_namespace_non_specific_platforms.platforms)
|
||||||
self.idl_namespace_non_specific_platforms.platforms)
|
|
||||||
|
|
||||||
def testInvalidNamespacePlatform(self):
|
def testInvalidNamespacePlatform(self):
|
||||||
invalid_namespace_platform = Load('test/invalid_platform_namespace.idl')
|
invalid_namespace_platform = Load('test/invalid_platform_namespace.idl')
|
||||||
@@ -225,7 +216,7 @@ class ModelTest(unittest.TestCase):
|
|||||||
def testPlatformsOnFunctionsIDL(self):
|
def testPlatformsOnFunctionsIDL(self):
|
||||||
function_win_linux = self.function_platforms.functions['function_win_linux']
|
function_win_linux = self.function_platforms.functions['function_win_linux']
|
||||||
self.assertEqual([Platforms.WIN, Platforms.LINUX],
|
self.assertEqual([Platforms.WIN, Platforms.LINUX],
|
||||||
function_win_linux.platforms)
|
function_win_linux.platforms)
|
||||||
|
|
||||||
function_all = self.function_platforms.functions['function_all']
|
function_all = self.function_platforms.functions['function_all']
|
||||||
self.assertIsNone(function_all.platforms)
|
self.assertIsNone(function_all.platforms)
|
||||||
@@ -269,5 +260,6 @@ class ModelTest(unittest.TestCase):
|
|||||||
self.assertIn('Enum value cannot be an empty string',
|
self.assertIn('Enum value cannot be an empty string',
|
||||||
str(context.exception))
|
str(context.exception))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@@ -18,7 +18,7 @@ def _GenerateFilenames(full_namespace):
|
|||||||
# 4. sub_name_space.idl,
|
# 4. sub_name_space.idl,
|
||||||
# 5. etc.
|
# 5. etc.
|
||||||
sub_namespaces = full_namespace.split('.')
|
sub_namespaces = full_namespace.split('.')
|
||||||
filenames = [ ]
|
filenames = []
|
||||||
basename = None
|
basename = None
|
||||||
for namespace in reversed(sub_namespaces):
|
for namespace in reversed(sub_namespaces):
|
||||||
if basename is not None:
|
if basename is not None:
|
||||||
@@ -39,6 +39,7 @@ class NamespaceResolver(object):
|
|||||||
used when searching for types.
|
used when searching for types.
|
||||||
- |cpp_namespace_pattern| Default namespace pattern
|
- |cpp_namespace_pattern| Default namespace pattern
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, root, path, include_rules, cpp_namespace_pattern):
|
def __init__(self, root, path, include_rules, cpp_namespace_pattern):
|
||||||
self._root = root
|
self._root = root
|
||||||
self._include_rules = [(path, cpp_namespace_pattern)] + include_rules
|
self._include_rules = [(path, cpp_namespace_pattern)] + include_rules
|
||||||
@@ -53,13 +54,12 @@ class NamespaceResolver(object):
|
|||||||
if cpp_namespace:
|
if cpp_namespace:
|
||||||
cpp_namespace_environment = CppNamespaceEnvironment(cpp_namespace)
|
cpp_namespace_environment = CppNamespaceEnvironment(cpp_namespace)
|
||||||
for filename in reversed(filenames):
|
for filename in reversed(filenames):
|
||||||
filepath = os.path.join(path, filename);
|
filepath = os.path.join(path, filename)
|
||||||
if os.path.exists(os.path.join(self._root, filepath)):
|
if os.path.exists(os.path.join(self._root, filepath)):
|
||||||
schema = SchemaLoader(self._root).LoadSchema(filepath)[0]
|
schema = SchemaLoader(self._root).LoadSchema(filepath)[0]
|
||||||
return Model().AddNamespace(
|
return Model().AddNamespace(schema,
|
||||||
schema,
|
filepath,
|
||||||
filepath,
|
environment=cpp_namespace_environment)
|
||||||
environment=cpp_namespace_environment)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def ResolveType(self, full_name, default_namespace):
|
def ResolveType(self, full_name, default_namespace):
|
||||||
|
@@ -17,8 +17,8 @@ import optparse
|
|||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import urlparse
|
import urlparse
|
||||||
from highlighters import (
|
from highlighters import (pygments_highlighter, none_highlighter,
|
||||||
pygments_highlighter, none_highlighter, hilite_me_highlighter)
|
hilite_me_highlighter)
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||||
from cpp_namespace_environment import CppNamespaceEnvironment
|
from cpp_namespace_environment import CppNamespaceEnvironment
|
||||||
from namespace_resolver import NamespaceResolver
|
from namespace_resolver import NamespaceResolver
|
||||||
@@ -27,6 +27,7 @@ from namespace_resolver import NamespaceResolver
|
|||||||
class CompilerHandler(BaseHTTPRequestHandler):
|
class CompilerHandler(BaseHTTPRequestHandler):
|
||||||
"""A HTTPRequestHandler that outputs the result of tools/json_schema_compiler.
|
"""A HTTPRequestHandler that outputs the result of tools/json_schema_compiler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
parsed_url = urlparse.urlparse(self.path)
|
parsed_url = urlparse.urlparse(self.path)
|
||||||
request_path = self._GetRequestPath(parsed_url)
|
request_path = self._GetRequestPath(parsed_url)
|
||||||
@@ -64,38 +65,36 @@ class CompilerHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
Code panes are populated via XHR after links in the nav pane are clicked.
|
Code panes are populated via XHR after links in the nav pane are clicked.
|
||||||
"""
|
"""
|
||||||
(head.Append('<style>')
|
(head.Append('<style>') \
|
||||||
.Append('body {')
|
.Append('body {') \
|
||||||
.Append(' margin: 0;')
|
.Append(' margin: 0;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('.pane {')
|
.Append('.pane {') \
|
||||||
.Append(' height: 100%;')
|
.Append(' height: 100%;') \
|
||||||
.Append(' overflow-x: auto;')
|
.Append(' overflow-x: auto;') \
|
||||||
.Append(' overflow-y: scroll;')
|
.Append(' overflow-y: scroll;') \
|
||||||
.Append(' display: inline-block;')
|
.Append(' display: inline-block;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('#nav_pane {')
|
.Append('#nav_pane {') \
|
||||||
.Append(' width: 20%;')
|
.Append(' width: 20%;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('#nav_pane ul {')
|
.Append('#nav_pane ul {') \
|
||||||
.Append(' list-style-type: none;')
|
.Append(' list-style-type: none;') \
|
||||||
.Append(' padding: 0 0 0 1em;')
|
.Append(' padding: 0 0 0 1em;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('#cc_pane {')
|
.Append('#cc_pane {') \
|
||||||
.Append(' width: 40%;')
|
.Append(' width: 40%;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('#h_pane {')
|
.Append('#h_pane {') \
|
||||||
.Append(' width: 40%;')
|
.Append(' width: 40%;') \
|
||||||
.Append('}')
|
.Append('}') \
|
||||||
.Append('</style>')
|
.Append('</style>')
|
||||||
)
|
)
|
||||||
|
|
||||||
body.Append(
|
body.Append('<div class="pane" id="nav_pane">%s</div>'
|
||||||
'<div class="pane" id="nav_pane">%s</div>'
|
'<div class="pane" id="h_pane"></div>'
|
||||||
'<div class="pane" id="h_pane"></div>'
|
'<div class="pane" id="cc_pane"></div>' %
|
||||||
'<div class="pane" id="cc_pane"></div>' %
|
self._RenderNavPane(parsed_url.path[1:]))
|
||||||
self._RenderNavPane(parsed_url.path[1:])
|
|
||||||
)
|
|
||||||
|
|
||||||
# The Javascript that interacts with the nav pane and panes to show the
|
# The Javascript that interacts with the nav pane and panes to show the
|
||||||
# compiled files as the URL or highlighting options change.
|
# compiled files as the URL or highlighting options change.
|
||||||
@@ -187,41 +186,39 @@ updateEverything();
|
|||||||
(file_root, file_ext) = os.path.splitext(request_path)
|
(file_root, file_ext) = os.path.splitext(request_path)
|
||||||
(filedir, filename) = os.path.split(file_root)
|
(filedir, filename) = os.path.split(file_root)
|
||||||
|
|
||||||
namespace_resolver = NamespaceResolver("./",
|
namespace_resolver = NamespaceResolver("./", filedir,
|
||||||
filedir,
|
|
||||||
self.server.include_rules,
|
self.server.include_rules,
|
||||||
self.server.cpp_namespace_pattern)
|
self.server.cpp_namespace_pattern)
|
||||||
try:
|
try:
|
||||||
# Get main file.
|
# Get main file.
|
||||||
namespace = namespace_resolver.ResolveNamespace(filename)
|
namespace = namespace_resolver.ResolveNamespace(filename)
|
||||||
type_generator = cpp_type_generator.CppTypeGenerator(
|
type_generator = cpp_type_generator.CppTypeGenerator(
|
||||||
api_model,
|
api_model, namespace_resolver, namespace)
|
||||||
namespace_resolver,
|
|
||||||
namespace)
|
|
||||||
|
|
||||||
# Generate code
|
# Generate code
|
||||||
if file_ext == '.h':
|
if file_ext == '.h':
|
||||||
cpp_code = (h_generator.HGenerator(type_generator)
|
cpp_code = (
|
||||||
.Generate(namespace).Render())
|
h_generator.HGenerator(type_generator).Generate(namespace).Render())
|
||||||
elif file_ext == '.cc':
|
elif file_ext == '.cc':
|
||||||
cpp_code = (cc_generator.CCGenerator(type_generator)
|
cpp_code = (cc_generator.CCGenerator(type_generator).Generate(
|
||||||
.Generate(namespace).Render())
|
namespace).Render())
|
||||||
else:
|
else:
|
||||||
self.send_error(404, "File not found: %s" % request_path)
|
self.send_error(404, "File not found: %s" % request_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Do highlighting on the generated code
|
# Do highlighting on the generated code
|
||||||
(highlighter_param, style_param) = self._GetHighlighterParams(parsed_url)
|
(highlighter_param, style_param) = self._GetHighlighterParams(parsed_url)
|
||||||
head.Append('<style>' +
|
head.Append(
|
||||||
|
'<style>' +
|
||||||
self.server.highlighters[highlighter_param].GetCSS(style_param) +
|
self.server.highlighters[highlighter_param].GetCSS(style_param) +
|
||||||
'</style>')
|
'</style>')
|
||||||
body.Append(self.server.highlighters[highlighter_param]
|
body.Append(self.server.highlighters[highlighter_param].GetCodeElement(
|
||||||
.GetCodeElement(cpp_code, style_param))
|
cpp_code, style_param))
|
||||||
except IOError:
|
except IOError:
|
||||||
self.send_error(404, "File not found: %s" % request_path)
|
self.send_error(404, "File not found: %s" % request_path)
|
||||||
return
|
return
|
||||||
except (TypeError, KeyError, AttributeError,
|
except (TypeError, KeyError, AttributeError, AssertionError,
|
||||||
AssertionError, NotImplementedError) as error:
|
NotImplementedError) as error:
|
||||||
body.Append('<pre>')
|
body.Append('<pre>')
|
||||||
body.Append('compiler error: %s' % error)
|
body.Append('compiler error: %s' % error)
|
||||||
body.Append('Check server log for more details')
|
body.Append('Check server log for more details')
|
||||||
@@ -233,7 +230,7 @@ updateEverything();
|
|||||||
"""
|
"""
|
||||||
query_dict = urlparse.parse_qs(parsed_url.query)
|
query_dict = urlparse.parse_qs(parsed_url.query)
|
||||||
return (query_dict.get('highlighter', ['pygments'])[0],
|
return (query_dict.get('highlighter', ['pygments'])[0],
|
||||||
query_dict.get('style', ['colorful'])[0])
|
query_dict.get('style', ['colorful'])[0])
|
||||||
|
|
||||||
def _RenderNavPane(self, path):
|
def _RenderNavPane(self, path):
|
||||||
"""Renders an HTML nav pane.
|
"""Renders an HTML nav pane.
|
||||||
@@ -248,7 +245,7 @@ updateEverything();
|
|||||||
html.Append('<select id="highlighters" onChange="updateEverything()">')
|
html.Append('<select id="highlighters" onChange="updateEverything()">')
|
||||||
for name, highlighter in self.server.highlighters.items():
|
for name, highlighter in self.server.highlighters.items():
|
||||||
html.Append('<option value="%s">%s</option>' %
|
html.Append('<option value="%s">%s</option>' %
|
||||||
(name, highlighter.DisplayName()))
|
(name, highlighter.DisplayName()))
|
||||||
html.Append('</select>')
|
html.Append('</select>')
|
||||||
|
|
||||||
html.Append('<br/>')
|
html.Append('<br/>')
|
||||||
@@ -289,8 +286,7 @@ updateEverything();
|
|||||||
html.Append('<li><a href="/%s/">%s/</a>' % (full_path, filename))
|
html.Append('<li><a href="/%s/">%s/</a>' % (full_path, filename))
|
||||||
elif file_ext in ['.json', '.idl']:
|
elif file_ext in ['.json', '.idl']:
|
||||||
# cc/h panes will automatically update via the hash change event.
|
# cc/h panes will automatically update via the hash change event.
|
||||||
html.Append('<li><a href="#%s">%s</a>' %
|
html.Append('<li><a href="#%s">%s</a>' % (filename, filename))
|
||||||
(filename, filename))
|
|
||||||
|
|
||||||
html.Append('</ul>')
|
html.Append('</ul>')
|
||||||
|
|
||||||
@@ -298,11 +294,8 @@ updateEverything();
|
|||||||
|
|
||||||
|
|
||||||
class PreviewHTTPServer(HTTPServer, object):
|
class PreviewHTTPServer(HTTPServer, object):
|
||||||
def __init__(self,
|
|
||||||
server_address,
|
def __init__(self, server_address, handler, highlighters, include_rules,
|
||||||
handler,
|
|
||||||
highlighters,
|
|
||||||
include_rules,
|
|
||||||
cpp_namespace_pattern):
|
cpp_namespace_pattern):
|
||||||
super(PreviewHTTPServer, self).__init__(server_address, handler)
|
super(PreviewHTTPServer, self).__init__(server_address, handler)
|
||||||
self.highlighters = highlighters
|
self.highlighters = highlighters
|
||||||
@@ -314,11 +307,18 @@ if __name__ == '__main__':
|
|||||||
parser = optparse.OptionParser(
|
parser = optparse.OptionParser(
|
||||||
description='Runs a server to preview the json_schema_compiler output.',
|
description='Runs a server to preview the json_schema_compiler output.',
|
||||||
usage='usage: %prog [option]...')
|
usage='usage: %prog [option]...')
|
||||||
parser.add_option('-p', '--port', default='8000',
|
parser.add_option('-p',
|
||||||
help='port to run the server on')
|
'--port',
|
||||||
parser.add_option('-n', '--namespace', default='generated_api_schemas',
|
default='8000',
|
||||||
|
help='port to run the server on')
|
||||||
|
parser.add_option(
|
||||||
|
'-n',
|
||||||
|
'--namespace',
|
||||||
|
default='generated_api_schemas',
|
||||||
help='C++ namespace for generated files. e.g extensions::api.')
|
help='C++ namespace for generated files. e.g extensions::api.')
|
||||||
parser.add_option('-I', '--include-rules',
|
parser.add_option(
|
||||||
|
'-I',
|
||||||
|
'--include-rules',
|
||||||
help='A list of paths to include when searching for referenced objects,'
|
help='A list of paths to include when searching for referenced objects,'
|
||||||
' with the namespace separated by a \':\'. Example: '
|
' with the namespace separated by a \':\'. Example: '
|
||||||
'/foo/bar:Foo::Bar::%(namespace)s')
|
'/foo/bar:Foo::Bar::%(namespace)s')
|
||||||
@@ -344,19 +344,16 @@ if __name__ == '__main__':
|
|||||||
print('')
|
print('')
|
||||||
|
|
||||||
highlighters = {
|
highlighters = {
|
||||||
'hilite': hilite_me_highlighter.HiliteMeHighlighter(),
|
'hilite': hilite_me_highlighter.HiliteMeHighlighter(),
|
||||||
'none': none_highlighter.NoneHighlighter()
|
'none': none_highlighter.NoneHighlighter()
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter()
|
highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter()
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
server = PreviewHTTPServer(('', int(opts.port)),
|
server = PreviewHTTPServer(('', int(opts.port)), CompilerHandler,
|
||||||
CompilerHandler,
|
highlighters, include_rules, opts.namespace)
|
||||||
highlighters,
|
|
||||||
include_rules,
|
|
||||||
opts.namespace)
|
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
server.socket.close()
|
server.socket.close()
|
||||||
|
@@ -8,10 +8,12 @@ import sys
|
|||||||
import idl_schema
|
import idl_schema
|
||||||
import json_schema
|
import json_schema
|
||||||
|
|
||||||
|
|
||||||
class SchemaLoader(object):
|
class SchemaLoader(object):
|
||||||
'''Loads a schema from a provided filename.
|
'''Loads a schema from a provided filename.
|
||||||
|root|: path to the root directory.
|
|root|: path to the root directory.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self._root = root
|
self._root = root
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
"""Utilies for the processing of schema python structures.
|
"""Utilies for the processing of schema python structures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def CapitalizeFirstLetter(value):
|
def CapitalizeFirstLetter(value):
|
||||||
return value[0].capitalize() + value[1:]
|
return value[0].capitalize() + value[1:]
|
||||||
|
|
||||||
|
@@ -7,7 +7,9 @@ from schema_util import JsFunctionNameToClassName
|
|||||||
from schema_util import StripNamespace
|
from schema_util import StripNamespace
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class SchemaUtilTest(unittest.TestCase):
|
class SchemaUtilTest(unittest.TestCase):
|
||||||
|
|
||||||
def testStripNamespace(self):
|
def testStripNamespace(self):
|
||||||
self.assertEqual('Bar', StripNamespace('foo.Bar'))
|
self.assertEqual('Bar', StripNamespace('foo.Bar'))
|
||||||
self.assertEqual('Baz', StripNamespace('Baz'))
|
self.assertEqual('Baz', StripNamespace('Baz'))
|
||||||
@@ -15,11 +17,11 @@ class SchemaUtilTest(unittest.TestCase):
|
|||||||
def testJsFunctionNameToClassName(self):
|
def testJsFunctionNameToClassName(self):
|
||||||
self.assertEqual('FooBar', JsFunctionNameToClassName('foo', 'bar'))
|
self.assertEqual('FooBar', JsFunctionNameToClassName('foo', 'bar'))
|
||||||
self.assertEqual('FooBar',
|
self.assertEqual('FooBar',
|
||||||
JsFunctionNameToClassName('experimental.foo', 'bar'))
|
JsFunctionNameToClassName('experimental.foo', 'bar'))
|
||||||
|
self.assertEqual('FooBarBaz', JsFunctionNameToClassName('foo.bar', 'baz'))
|
||||||
self.assertEqual('FooBarBaz',
|
self.assertEqual('FooBarBaz',
|
||||||
JsFunctionNameToClassName('foo.bar', 'baz'))
|
JsFunctionNameToClassName('experimental.foo.bar', 'baz'))
|
||||||
self.assertEqual('FooBarBaz',
|
|
||||||
JsFunctionNameToClassName('experimental.foo.bar', 'baz'))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
# Copyright 2023 The Chromium Authors
|
# Copyright 2023 The Chromium Authors
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
# Use of this source code is governed by a BSD-style license that can be
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
"""Generator that produces a definition file for typescript.
|
"""Generator that produces a definition file for typescript.
|
||||||
|
|
||||||
Note: This is a work in progress, and generated definitions may need tweaking.
|
Note: This is a work in progress, and generated definitions may need tweaking.
|
||||||
@@ -19,7 +18,6 @@ from js_util import JsUtil
|
|||||||
from model import *
|
from model import *
|
||||||
from schema_util import *
|
from schema_util import *
|
||||||
|
|
||||||
|
|
||||||
CHROMIUM_SRC = os.path.abspath(
|
CHROMIUM_SRC = os.path.abspath(
|
||||||
os.path.join(os.path.dirname(__file__), "..", ".."))
|
os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||||
|
|
||||||
@@ -59,8 +57,7 @@ class _Generator(object):
|
|||||||
# If events are needed, add the import.
|
# If events are needed, add the import.
|
||||||
if self._events_required:
|
if self._events_required:
|
||||||
main_code.Substitute(
|
main_code.Substitute(
|
||||||
{"imports": "import {ChromeEvent} from './chrome_event.js';"}
|
{"imports": "import {ChromeEvent} from './chrome_event.js';"})
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
main_code.Substitute({"imports": ""})
|
main_code.Substitute({"imports": ""})
|
||||||
main_code = self._ClangFormat(main_code)
|
main_code = self._ClangFormat(main_code)
|
||||||
@@ -111,10 +108,8 @@ class _Generator(object):
|
|||||||
type_name = self._ExtractType(prop.type_)
|
type_name = self._ExtractType(prop.type_)
|
||||||
# If the ref type has additional properties, do a namespace merge.
|
# If the ref type has additional properties, do a namespace merge.
|
||||||
prop_type: Type = prop.type_
|
prop_type: Type = prop.type_
|
||||||
if (
|
if (len(prop_type.properties) > 0
|
||||||
len(prop_type.properties) > 0
|
and prop_type.property_type == PropertyType.REF):
|
||||||
and prop_type.property_type == PropertyType.REF
|
|
||||||
):
|
|
||||||
type_name = self._AppendInterfaceForProperty(c, prop, type_name)
|
type_name = self._AppendInterfaceForProperty(c, prop, type_name)
|
||||||
c.Append(f"export const {prop.name}: {type_name};")
|
c.Append(f"export const {prop.name}: {type_name};")
|
||||||
c.Append()
|
c.Append()
|
||||||
@@ -143,9 +138,8 @@ class _Generator(object):
|
|||||||
|
|
||||||
# This appends an local only interface to allow for additional
|
# This appends an local only interface to allow for additional
|
||||||
# properties on an already defined type.
|
# properties on an already defined type.
|
||||||
def _AppendInterfaceForProperty(
|
def _AppendInterfaceForProperty(self, c: Code, prop: Property,
|
||||||
self, c: Code, prop: Property, prop_type_name
|
prop_type_name):
|
||||||
):
|
|
||||||
if prop.deprecated:
|
if prop.deprecated:
|
||||||
return
|
return
|
||||||
prop_type = prop.type_
|
prop_type = prop.type_
|
||||||
@@ -177,16 +171,15 @@ class _Generator(object):
|
|||||||
# Type alias
|
# Type alias
|
||||||
c.Append(f"export type {type.name} = {type.property_type.name};")
|
c.Append(f"export type {type.name} = {type.property_type.name};")
|
||||||
c.Append()
|
c.Append()
|
||||||
elif (type.property_type is PropertyType.ARRAY or
|
elif (type.property_type is PropertyType.ARRAY
|
||||||
type.property_type is PropertyType.CHOICES) :
|
or type.property_type is PropertyType.CHOICES):
|
||||||
ts_type = self._ExtractType(type)
|
ts_type = self._ExtractType(type)
|
||||||
c.Append(f"export type {type.name} = {ts_type};")
|
c.Append(f"export type {type.name} = {ts_type};")
|
||||||
c.Append()
|
c.Append()
|
||||||
else:
|
else:
|
||||||
# Adding this for things we may not have accounted for here.
|
# Adding this for things we may not have accounted for here.
|
||||||
c.Append(
|
c.Append(
|
||||||
f"// TODO({os.getlogin()}) -- {type.name}: {type.property_type.name}"
|
f"// TODO({os.getlogin()}) -- {type.name}: {type.property_type.name}")
|
||||||
)
|
|
||||||
|
|
||||||
def _AppendInterface(self, c: Code, interface: Type):
|
def _AppendInterface(self, c: Code, interface: Type):
|
||||||
c.Sblock(f"export interface {interface.name} {{")
|
c.Sblock(f"export interface {interface.name} {{")
|
||||||
@@ -231,8 +224,8 @@ class _Generator(object):
|
|||||||
ret_type = "void"
|
ret_type = "void"
|
||||||
if func.returns is not None:
|
if func.returns is not None:
|
||||||
ret_type = self._ExtractType(func.returns)
|
ret_type = self._ExtractType(func.returns)
|
||||||
elif (func.returns_async is not None and
|
elif (func.returns_async is not None
|
||||||
func.returns_async.can_return_promise):
|
and func.returns_async.can_return_promise):
|
||||||
ret_type = f"Promise<{self._ExtractPromiseType(func.returns_async)}>"
|
ret_type = f"Promise<{self._ExtractPromiseType(func.returns_async)}>"
|
||||||
return ret_type
|
return ret_type
|
||||||
|
|
||||||
@@ -328,9 +321,8 @@ class _Generator(object):
|
|||||||
|
|
||||||
# When the return async isn't a promise, we append it as a return callback
|
# When the return async isn't a promise, we append it as a return callback
|
||||||
# at the end of the parameters.
|
# at the end of the parameters.
|
||||||
use_callback = (
|
use_callback = (func.returns_async
|
||||||
func.returns_async and not func.returns_async.can_return_promise
|
and not func.returns_async.can_return_promise)
|
||||||
)
|
|
||||||
if use_callback:
|
if use_callback:
|
||||||
callback_params = self._ExtractParams(func.returns_async.params)
|
callback_params = self._ExtractParams(func.returns_async.params)
|
||||||
if param_str:
|
if param_str:
|
||||||
@@ -378,24 +370,21 @@ class _Generator(object):
|
|||||||
def _ClangFormat(self, c: Code, level=0):
|
def _ClangFormat(self, c: Code, level=0):
|
||||||
# temp = tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js")
|
# temp = tempfile.NamedTemporaryFile("w", encoding="utf-8", suffix=".js")
|
||||||
# f_name = temp.name
|
# f_name = temp.name
|
||||||
with tempfile.NamedTemporaryFile(
|
with tempfile.NamedTemporaryFile("w",
|
||||||
"w", encoding="utf-8", suffix=".js", delete=False
|
encoding="utf-8",
|
||||||
) as f:
|
suffix=".js",
|
||||||
|
delete=False) as f:
|
||||||
f.write(c.Render())
|
f.write(c.Render())
|
||||||
f_name = f.name
|
f_name = f.name
|
||||||
script_path = self._GetChromiumClangFormatScriptPath()
|
script_path = self._GetChromiumClangFormatScriptPath()
|
||||||
style_path = self._GetChromiumClangFormatStylePath()
|
style_path = self._GetChromiumClangFormatStylePath()
|
||||||
cmd = (
|
cmd = (f'python3 {script_path} --fallback-style=none '
|
||||||
f'python3 {script_path} --fallback-style=none '
|
f'--style=file:{style_path} "{f_name}"')
|
||||||
f'--style=file:{style_path} "{f_name}"'
|
p = subprocess.Popen(cmd,
|
||||||
)
|
cwd=CHROMIUM_SRC,
|
||||||
p = subprocess.Popen(
|
encoding="utf-8",
|
||||||
cmd,
|
shell=True,
|
||||||
cwd=CHROMIUM_SRC,
|
stdout=subprocess.PIPE)
|
||||||
encoding="utf-8",
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE
|
|
||||||
)
|
|
||||||
out = p.communicate()[0]
|
out = p.communicate()[0]
|
||||||
out_code = Code()
|
out_code = Code()
|
||||||
out_code.Append(out)
|
out_code.Append(out)
|
||||||
|
@@ -17,11 +17,8 @@ class TsDefinitionGeneratorTest(unittest.TestCase):
|
|||||||
def _GetNamespace(self, fake_content, filename):
|
def _GetNamespace(self, fake_content, filename):
|
||||||
"""Returns a namespace object for the given content"""
|
"""Returns a namespace object for the given content"""
|
||||||
is_idl = filename.endswith('.idl')
|
is_idl = filename.endswith('.idl')
|
||||||
api_def = (
|
api_def = (idl_schema.Process(fake_content, filename)
|
||||||
idl_schema.Process(fake_content, filename)
|
if is_idl else json_parse.Parse(fake_content))
|
||||||
if is_idl
|
|
||||||
else json_parse.Parse(fake_content)
|
|
||||||
)
|
|
||||||
m = model.Model()
|
m = model.Model()
|
||||||
return m.AddNamespace(api_def[0], filename)
|
return m.AddNamespace(api_def[0], filename)
|
||||||
|
|
||||||
|
@@ -9,14 +9,15 @@ class UtilCCHelper(object):
|
|||||||
"""A util class that generates code that uses
|
"""A util class that generates code that uses
|
||||||
tools/json_schema_compiler/util.cc.
|
tools/json_schema_compiler/util.cc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, type_manager):
|
def __init__(self, type_manager):
|
||||||
self._type_manager = type_manager
|
self._type_manager = type_manager
|
||||||
|
|
||||||
def PopulateArrayFromListFunction(self, optional):
|
def PopulateArrayFromListFunction(self, optional):
|
||||||
"""Returns the function to turn a list into a vector.
|
"""Returns the function to turn a list into a vector.
|
||||||
"""
|
"""
|
||||||
populate_list_fn = ('PopulateOptionalArrayFromList' if optional
|
populate_list_fn = ('PopulateOptionalArrayFromList'
|
||||||
else 'PopulateArrayFromList')
|
if optional else 'PopulateArrayFromList')
|
||||||
return ('%s::%s') % (_API_UTIL_NAMESPACE, populate_list_fn)
|
return ('%s::%s') % (_API_UTIL_NAMESPACE, populate_list_fn)
|
||||||
|
|
||||||
def CreateValueFromArray(self, src):
|
def CreateValueFromArray(self, src):
|
||||||
@@ -29,8 +30,8 @@ class UtilCCHelper(object):
|
|||||||
def AppendToContainer(self, container, value):
|
def AppendToContainer(self, container, value):
|
||||||
"""Appends |value| to |container|.
|
"""Appends |value| to |container|.
|
||||||
"""
|
"""
|
||||||
return '%s::AppendToContainer(%s, %s);' % (
|
return '%s::AppendToContainer(%s, %s);' % (_API_UTIL_NAMESPACE, container,
|
||||||
_API_UTIL_NAMESPACE, container, value)
|
value)
|
||||||
|
|
||||||
def GetIncludePath(self):
|
def GetIncludePath(self):
|
||||||
return '#include "tools/json_schema_compiler/util.h"'
|
return '#include "tools/json_schema_compiler/util.h"'
|
||||||
|
@@ -21,9 +21,8 @@ from json_parse import OrderedDict
|
|||||||
|
|
||||||
# idl_parser expects to be able to import certain files in its directory,
|
# idl_parser expects to be able to import certain files in its directory,
|
||||||
# so let's set things up the way it wants.
|
# so let's set things up the way it wants.
|
||||||
_idl_generators_path = os.path.join(
|
_idl_generators_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||||
os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, 'tools'
|
os.pardir, os.pardir, 'tools')
|
||||||
)
|
|
||||||
if _idl_generators_path in sys.path:
|
if _idl_generators_path in sys.path:
|
||||||
from idl_parser import idl_parser, idl_lexer, idl_node
|
from idl_parser import idl_parser, idl_lexer, idl_node
|
||||||
else:
|
else:
|
||||||
@@ -40,8 +39,7 @@ class SchemaCompilerError(Exception):
|
|||||||
|
|
||||||
def __init__(self, message: str, node: IDLNode):
|
def __init__(self, message: str, node: IDLNode):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
node.GetLogLine(f'Error processing node {node}: {message}')
|
node.GetLogLine(f'Error processing node {node}: {message}'))
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def GetChildWithName(node: IDLNode, name: str) -> Optional[IDLNode]:
|
def GetChildWithName(node: IDLNode, name: str) -> Optional[IDLNode]:
|
||||||
@@ -56,8 +54,7 @@ def GetChildWithName(node: IDLNode, name: str) -> Optional[IDLNode]:
|
|||||||
name was not found.
|
name was not found.
|
||||||
"""
|
"""
|
||||||
return next(
|
return next(
|
||||||
(child for child in node.GetChildren() if child.GetName() == name), None
|
(child for child in node.GetChildren() if child.GetName() == name), None)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def GetTypeName(node: IDLNode) -> str:
|
def GetTypeName(node: IDLNode) -> str:
|
||||||
@@ -76,8 +73,7 @@ def GetTypeName(node: IDLNode) -> str:
|
|||||||
if child_node.GetClass() == 'Type':
|
if child_node.GetClass() == 'Type':
|
||||||
return child_node.GetOneOf('Typeref').GetName()
|
return child_node.GetOneOf('Typeref').GetName()
|
||||||
raise SchemaCompilerError(
|
raise SchemaCompilerError(
|
||||||
'Could not find Type node when looking for Typeref name.', node
|
'Could not find Type node when looking for Typeref name.', node)
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def GetExtendedAttributes(node: IDLNode) -> Optional[List[IDLNode]]:
|
def GetExtendedAttributes(node: IDLNode) -> Optional[List[IDLNode]]:
|
||||||
@@ -111,9 +107,8 @@ class Type:
|
|||||||
|
|
||||||
def __init__(self, node: IDLNode, additional_properties: dict) -> None:
|
def __init__(self, node: IDLNode, additional_properties: dict) -> None:
|
||||||
assert node.GetClass() == 'Type', node.GetLogLine(
|
assert node.GetClass() == 'Type', node.GetLogLine(
|
||||||
'Attempted to process a "Type" node, but was passed a "%s" node.'
|
'Attempted to process a "Type" node, but was passed a "%s" node.' %
|
||||||
% (node.GetClass())
|
(node.GetClass()))
|
||||||
)
|
|
||||||
self.node = node
|
self.node = node
|
||||||
self.additional_properties = additional_properties
|
self.additional_properties = additional_properties
|
||||||
|
|
||||||
@@ -136,13 +131,11 @@ class Type:
|
|||||||
properties['type'] = 'string'
|
properties['type'] = 'string'
|
||||||
else:
|
else:
|
||||||
raise SchemaCompilerError(
|
raise SchemaCompilerError(
|
||||||
'Unsupported basic type found when processing type.', basic_type
|
'Unsupported basic type found when processing type.', basic_type)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
unknown_child = self.node.GetChildren()[0]
|
unknown_child = self.node.GetChildren()[0]
|
||||||
raise SchemaCompilerError(
|
raise SchemaCompilerError('Unsupported type class when processing type.',
|
||||||
'Unsupported type class when processing type.', unknown_child
|
unknown_child)
|
||||||
)
|
|
||||||
|
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
@@ -241,8 +234,7 @@ class IDLSchema:
|
|||||||
browser_node = GetChildWithName(self.idl, 'Browser')
|
browser_node = GetChildWithName(self.idl, 'Browser')
|
||||||
if browser_node is None or browser_node.GetClass() != 'Interface':
|
if browser_node is None or browser_node.GetClass() != 'Interface':
|
||||||
raise SchemaCompilerError(
|
raise SchemaCompilerError(
|
||||||
'Required partial Browser interface not found in schema.', self.idl
|
'Required partial Browser interface not found in schema.', self.idl)
|
||||||
)
|
|
||||||
|
|
||||||
# The 'Browser' Interface has one attribute describing the name this API is
|
# The 'Browser' Interface has one attribute describing the name this API is
|
||||||
# exposed on.
|
# exposed on.
|
||||||
@@ -298,9 +290,8 @@ def Main():
|
|||||||
for i, char in enumerate(contents):
|
for i, char in enumerate(contents):
|
||||||
if not char.isascii():
|
if not char.isascii():
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Non-ascii character "%s" (ord %d) found at offset %d.'
|
'Non-ascii character "%s" (ord %d) found at offset %d.' %
|
||||||
% (char, ord(char), i)
|
(char, ord(char), i))
|
||||||
)
|
|
||||||
idl = idl_parser.IDLParser().ParseData(contents, '<stdin>')
|
idl = idl_parser.IDLParser().ParseData(contents, '<stdin>')
|
||||||
schema = IDLSchema(idl).process()
|
schema = IDLSchema(idl).process()
|
||||||
print(json.dumps(schema, indent=2))
|
print(json.dumps(schema, indent=2))
|
||||||
|
@@ -36,19 +36,31 @@ class WebIdlSchemaTest(unittest.TestCase):
|
|||||||
getReturns(schema, 'returnsVoid'),
|
getReturns(schema, 'returnsVoid'),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'name': 'returnsBoolean', 'type': 'boolean'},
|
{
|
||||||
|
'name': 'returnsBoolean',
|
||||||
|
'type': 'boolean'
|
||||||
|
},
|
||||||
getReturns(schema, 'returnsBoolean'),
|
getReturns(schema, 'returnsBoolean'),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'name': 'returnsDouble', 'type': 'number'},
|
{
|
||||||
|
'name': 'returnsDouble',
|
||||||
|
'type': 'number'
|
||||||
|
},
|
||||||
getReturns(schema, 'returnsDouble'),
|
getReturns(schema, 'returnsDouble'),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'name': 'returnsLong', 'type': 'integer'},
|
{
|
||||||
|
'name': 'returnsLong',
|
||||||
|
'type': 'integer'
|
||||||
|
},
|
||||||
getReturns(schema, 'returnsLong'),
|
getReturns(schema, 'returnsLong'),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{'name': 'returnsDOMString', 'type': 'string'},
|
{
|
||||||
|
'name': 'returnsDOMString',
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
getReturns(schema, 'returnsDOMString'),
|
getReturns(schema, 'returnsDOMString'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,8 +74,7 @@ class WebIdlSchemaTest(unittest.TestCase):
|
|||||||
def testMissingBrowserInterface(self):
|
def testMissingBrowserInterface(self):
|
||||||
expected_error_regex = (
|
expected_error_regex = (
|
||||||
'.* File\(test\/web_idl\/missing_browser_interface.idl\): Required'
|
'.* File\(test\/web_idl\/missing_browser_interface.idl\): Required'
|
||||||
' partial Browser interface not found in schema\.'
|
' partial Browser interface not found in schema\.')
|
||||||
)
|
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
SchemaCompilerError,
|
SchemaCompilerError,
|
||||||
expected_error_regex,
|
expected_error_regex,
|
||||||
@@ -76,8 +87,7 @@ class WebIdlSchemaTest(unittest.TestCase):
|
|||||||
def testMissingAttributeOnBrowser(self):
|
def testMissingAttributeOnBrowser(self):
|
||||||
expected_error_regex = (
|
expected_error_regex = (
|
||||||
'.* Interface\(Browser\): The partial Browser interface should have'
|
'.* Interface\(Browser\): The partial Browser interface should have'
|
||||||
' exactly one attribute for the name the API will be exposed under\.'
|
' exactly one attribute for the name the API will be exposed under\.')
|
||||||
)
|
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
Exception,
|
Exception,
|
||||||
expected_error_regex,
|
expected_error_regex,
|
||||||
@@ -90,8 +100,7 @@ class WebIdlSchemaTest(unittest.TestCase):
|
|||||||
def testUnsupportedBasicType(self):
|
def testUnsupportedBasicType(self):
|
||||||
expected_error_regex = (
|
expected_error_regex = (
|
||||||
'.* PrimitiveType\(float\): Unsupported basic type found when'
|
'.* PrimitiveType\(float\): Unsupported basic type found when'
|
||||||
' processing type\.'
|
' processing type\.')
|
||||||
)
|
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
SchemaCompilerError,
|
SchemaCompilerError,
|
||||||
expected_error_regex,
|
expected_error_regex,
|
||||||
@@ -103,8 +112,7 @@ class WebIdlSchemaTest(unittest.TestCase):
|
|||||||
# doesn't support yet throws an error.
|
# doesn't support yet throws an error.
|
||||||
def testUnsupportedTypeClass(self):
|
def testUnsupportedTypeClass(self):
|
||||||
expected_error_regex = (
|
expected_error_regex = (
|
||||||
'.* Any\(\): Unsupported type class when processing type\.'
|
'.* Any\(\): Unsupported type class when processing type\.')
|
||||||
)
|
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
SchemaCompilerError,
|
SchemaCompilerError,
|
||||||
expected_error_regex,
|
expected_error_regex,
|
||||||
|
Reference in New Issue
Block a user