| Index: tools/json_schema_compiler/dart_generator.py
|
| diff --git a/tools/json_schema_compiler/dart_generator.py b/tools/json_schema_compiler/dart_generator.py
|
| index 9503ef337f1ba6845f3938de901783ddca474dc8..66db1357ee60518e5d4a63ff3db505cc25109dae 100644
|
| --- a/tools/json_schema_compiler/dart_generator.py
|
| +++ b/tools/json_schema_compiler/dart_generator.py
|
| @@ -12,8 +12,8 @@ from schema_util import *
|
| import os
|
| from datetime import datetime
|
|
|
| -LICENSE = ("""
|
| -// Copyright (c) %s, the Dart project authors. Please see the AUTHORS file
|
| +LICENSE = (
|
| +"""// Copyright (c) %s, the Dart project authors. Please see the AUTHORS file
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.""" %
|
| datetime.now().year)
|
| @@ -31,6 +31,9 @@ class _Generator(object):
|
|
|
| def __init__(self, namespace, dart_overrides_dir=None):
|
| self._namespace = namespace
|
| + # TODO(sashab): Once inline type definitions start being added to
|
| + # self._types, make a _FindType(self, type_) function that looks at
|
| + # self._namespace.types.
|
| self._types = namespace.types
|
|
|
| # Build a dictionary of Type Name --> Custom Dart code.
|
| @@ -43,11 +46,17 @@ class _Generator(object):
|
| type_path = '.'.join(filename.split('.')[1:-1])
|
| self._type_overrides[type_path] = f.read()
|
|
|
| + # TODO(sashab): Add all inline type definitions to the global Types
|
| + # dictionary here, so they have proper names, and are implemented along with
|
| + # all other types. Also update the parameters/members with these types
|
| + # to reference these new types instead.
|
| +
|
| def Generate(self):
|
| """Generates a Code object with the .dart for the entire namespace.
|
| """
|
| c = Code()
|
| (c.Append(LICENSE)
|
| + .Append()
|
| .Append('// Generated from namespace: %s' % self._namespace.name)
|
| .Append()
|
| .Append('part of chrome;'))
|
| @@ -57,25 +66,28 @@ class _Generator(object):
|
| .Append('/**')
|
| .Append(' * Types')
|
| .Append(' */')
|
| + .Append()
|
| )
|
| - for type_name in self._types:
|
| - c.Concat(self._GenerateType(self._types[type_name]))
|
| + for type_ in self._types.values():
|
| + # Check for custom dart for this whole type.
|
| + override = self._GetOverride([type_.name], document_with=type_)
|
| + c.Cblock(override if override is not None else self._GenerateType(type_))
|
|
|
| if self._namespace.events:
|
| - (c.Append()
|
| - .Append('/**')
|
| + (c.Append('/**')
|
| .Append(' * Events')
|
| .Append(' */')
|
| + .Append()
|
| )
|
| for event_name in self._namespace.events:
|
| - c.Concat(self._GenerateEvent(self._namespace.events[event_name]))
|
| + c.Cblock(self._GenerateEvent(self._namespace.events[event_name]))
|
|
|
| - (c.Append()
|
| - .Append('/**')
|
| + (c.Append('/**')
|
| .Append(' * Functions')
|
| .Append(' */')
|
| + .Append()
|
| )
|
| - c.Concat(self._GenerateMainClass())
|
| + c.Cblock(self._GenerateMainClass())
|
|
|
| return c
|
|
|
| @@ -89,8 +101,14 @@ class _Generator(object):
|
| setters that hide the JS() implementation.
|
| """
|
| c = Code()
|
| - (c.Append()
|
| - .Concat(self._GenerateDocumentation(type_))
|
| +
|
| + # Since enums are just treated as strings for now, don't generate their
|
| + # type.
|
| + # TODO(sashab): Find a nice way to wrap enum objects.
|
| + if type_.property_type is PropertyType.ENUM:
|
| + return c
|
| +
|
| + (c.Concat(self._GenerateDocumentation(type_))
|
| .Sblock('class %(type_name)s extends ChromeObject {')
|
| )
|
|
|
| @@ -109,7 +127,10 @@ class _Generator(object):
|
| )
|
|
|
| for prop_name in type_.properties:
|
| - c.Append('this.%s = %s;' % (prop_name, prop_name))
|
| + (c.Sblock('if (?%s)' % prop_name)
|
| + .Append('this.%s = %s;' % (prop_name, prop_name))
|
| + .Eblock()
|
| + )
|
| (c.Eblock('}')
|
| .Append()
|
| )
|
| @@ -130,59 +151,9 @@ class _Generator(object):
|
| .Append(' */')
|
| )
|
| for prop in properties:
|
| - type_name = self._GetDartType(prop.type_)
|
| -
|
| - # Check for custom dart for this whole property.
|
| - c.Append()
|
| - if not self._ConcatOverride(c, type_, prop, add_doc=True):
|
| - # Add the getter.
|
| - if not self._ConcatOverride(c, type_, prop, key_suffix='.get',
|
| - add_doc=True):
|
| - # Add the documentation for this property.
|
| - c.Concat(self._GenerateDocumentation(prop))
|
| -
|
| - if (self._IsBaseType(prop.type_)
|
| - or self._IsListOfBaseTypes(prop.type_)):
|
| - c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
|
| - (type_name, prop.name, type_name, prop.name))
|
| - elif self._IsSerializableObjectType(prop.type_):
|
| - c.Append("%s get %s => new %s._proxy(JS('', '#.%s', "
|
| - "this._jsObject));"
|
| - % (type_name, prop.name, type_name, prop.name))
|
| - elif self._IsListOfSerializableObjects(prop.type_):
|
| - (c.Sblock('%s get %s {' % (type_name, prop.name))
|
| - .Append('%s __proxy_%s = new %s();' % (type_name, prop.name,
|
| - type_name))
|
| - .Sblock("for (var o in JS('List', '#.%s', this._jsObject)) {" %
|
| - prop.name)
|
| - .Append('__proxy_%s.add(new %s._proxy(o));' % (prop.name,
|
| - self._GetDartType(prop.type_.item_type)))
|
| - .Eblock('}')
|
| - .Append('return __proxy_%s;' % prop.name)
|
| - .Eblock('}')
|
| - )
|
| - elif self._IsObjectType(prop.type_):
|
| - # TODO(sashab): Think of a way to serialize generic Dart objects.
|
| - c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
|
| - (type_name, prop.name, type_name, prop.name))
|
| - else:
|
| - raise Exception(
|
| - "Could not generate wrapper for %s.%s: unserializable type %s" %
|
| - (type_.name, prop.name, type_name)
|
| - )
|
| -
|
| - # Add the setter.
|
| - c.Append()
|
| - if not self._ConcatOverride(c, type_, prop, key_suffix='.set'):
|
| - wrapped_name = prop.name
|
| - if not self._IsBaseType(prop.type_):
|
| - wrapped_name = 'convertArgument(%s)' % prop.name
|
| -
|
| - (c.Sblock("void set %s(%s %s) {" % (prop.name, type_name, prop.name))
|
| - .Append("JS('void', '#.%s = #', this._jsObject, %s);" %
|
| - (prop.name, wrapped_name))
|
| - .Eblock("}")
|
| - )
|
| + override = self._GetOverride([type_.name, prop.name], document_with=prop)
|
| + c.Concat(override if override is not None
|
| + else self._GenerateGetterAndSetter(type_, prop))
|
|
|
| # Now add all the methods.
|
| methods = [t for t in type_.properties.values()
|
| @@ -194,17 +165,91 @@ class _Generator(object):
|
| .Append(' */')
|
| )
|
| for prop in methods:
|
| - c.Concat(self._GenerateFunction(prop.type_.function))
|
| + # Check if there's an override for this method.
|
| + override = self._GetOverride([type_.name, prop.name], document_with=prop)
|
| + c.Cblock(override if override is not None
|
| + else self._GenerateFunction(prop.type_.function))
|
|
|
| (c.Eblock('}')
|
| .Substitute({
|
| - 'type_name': type_.simple_name,
|
| + 'type_name': self._AddPrefix(type_.simple_name),
|
| 'constructor_fields': ', '.join(constructor_fields)
|
| })
|
| )
|
|
|
| return c
|
|
|
| + def _GenerateGetterAndSetter(self, type_, prop):
|
| + """Given a Type and Property, returns the Code object for the getter and
|
| + setter for that property.
|
| + """
|
| + c = Code()
|
| + override = self._GetOverride([type_.name, prop.name, '.get'],
|
| + document_with=prop)
|
| + c.Cblock(override if override is not None
|
| + else self._GenerateGetter(type_, prop))
|
| + override = self._GetOverride([type_.name, prop.name, '.set'])
|
| + c.Cblock(override if override is not None
|
| + else self._GenerateSetter(type_, prop))
|
| + return c
|
| +
|
| + def _GenerateGetter(self, type_, prop):
|
| + """Given a Type and Property, returns the Code object for the getter for
|
| + that property.
|
| +
|
| + Also adds the documentation for this property before the method.
|
| + """
|
| + c = Code()
|
| + c.Concat(self._GenerateDocumentation(prop))
|
| +
|
| + type_name = self._GetDartType(prop.type_)
|
| + if (self._IsBaseType(prop.type_)):
|
| + c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
|
| + (type_name, prop.name, type_name, prop.name))
|
| + elif self._IsSerializableObjectType(prop.type_):
|
| + c.Append("%s get %s => new %s._proxy(JS('', '#.%s', "
|
| + "this._jsObject));"
|
| + % (type_name, prop.name, type_name, prop.name))
|
| + elif self._IsListOfSerializableObjects(prop.type_):
|
| + (c.Sblock('%s get %s {' % (type_name, prop.name))
|
| + .Append('%s __proxy_%s = new %s();' % (type_name, prop.name,
|
| + type_name))
|
| + .Sblock("for (var o in JS('List', '#.%s', this._jsObject)) {" %
|
| + prop.name)
|
| + .Append('__proxy_%s.add(new %s._proxy(o));' % (prop.name,
|
| + self._GetDartType(prop.type_.item_type)))
|
| + .Eblock('}')
|
| + .Append('return __proxy_%s;' % prop.name)
|
| + .Eblock('}')
|
| + )
|
| + elif self._IsObjectType(prop.type_):
|
| + # TODO(sashab): Think of a way to serialize generic Dart objects.
|
| + c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
|
| + (type_name, prop.name, type_name, prop.name))
|
| + else:
|
| + raise Exception(
|
| + "Could not generate wrapper for %s.%s: unserializable type %s" %
|
| + (type_.name, prop.name, type_name)
|
| + )
|
| + return c
|
| +
|
| + def _GenerateSetter(self, type_, prop):
|
| + """Given a Type and Property, returns the Code object for the setter for
|
| + that property.
|
| + """
|
| + c = Code()
|
| + type_name = self._GetDartType(prop.type_)
|
| + wrapped_name = prop.name
|
| + if not self._IsBaseType(prop.type_):
|
| + wrapped_name = 'convertArgument(%s)' % prop.name
|
| +
|
| + (c.Sblock("void set %s(%s %s) {" % (prop.name, type_name, prop.name))
|
| + .Append("JS('void', '#.%s = #', this._jsObject, %s);" %
|
| + (prop.name, wrapped_name))
|
| + .Eblock("}")
|
| + )
|
| + return c
|
| +
|
| def _GenerateDocumentation(self, prop):
|
| """Given an object, generates the documentation for this object (as a
|
| code string) and returns the Code object.
|
| @@ -223,9 +268,7 @@ class _Generator(object):
|
| """Returns the Code object for the given function.
|
| """
|
| c = Code()
|
| - (c.Append()
|
| - .Concat(self._GenerateDocumentation(f))
|
| - )
|
| + c.Concat(self._GenerateDocumentation(f))
|
|
|
| if not self._NeedsProxiedCallback(f):
|
| c.Append("%s => %s;" % (self._GenerateFunctionSignature(f),
|
| @@ -252,7 +295,7 @@ class _Generator(object):
|
| # their members (by copying out each member and proxying it).
|
| lists_to_proxy = []
|
| for p in f.params:
|
| - if self._IsBaseType(p.type_) or self._IsListOfBaseTypes(p.type_):
|
| + if self._IsBaseType(p.type_):
|
| proxied_params.append(p.name)
|
| elif self._IsSerializableObjectType(p.type_):
|
| proxied_params.append('new %s._proxy(%s)' % (
|
| @@ -263,6 +306,11 @@ class _Generator(object):
|
| elif self._IsObjectType(p.type_):
|
| # TODO(sashab): Find a way to build generic JS objects back in Dart.
|
| proxied_params.append('%s' % p.name)
|
| + elif p.type_.property_type is PropertyType.ARRAY:
|
| + # TODO(sashab): This might be okay - what if this is a list of
|
| + # FileEntry elements? In this case, a basic list will proxy the objects
|
| + # fine.
|
| + proxied_params.append('%s' % p.name)
|
| else:
|
| raise Exception(
|
| "Cannot automatically create proxy; can't wrap %s, type %s" % (
|
| @@ -353,8 +401,7 @@ class _Generator(object):
|
| c = Code()
|
|
|
| # Add documentation for this event.
|
| - (c.Append()
|
| - .Concat(self._GenerateDocumentation(event))
|
| + (c.Concat(self._GenerateDocumentation(event))
|
| .Sblock('class Event_%(event_name)s extends Event {')
|
| )
|
|
|
| @@ -381,7 +428,7 @@ class _Generator(object):
|
|
|
| # Generate the constructor.
|
| (c.Append('Event_%(event_name)s(jsObject) : '
|
| - 'super(jsObject, %(param_num)d);')
|
| + 'super._(jsObject, %(param_num)d);')
|
| .Eblock('}')
|
| .Substitute({
|
| 'event_name': self._namespace.unix_name + '_' + event.name,
|
| @@ -398,8 +445,7 @@ class _Generator(object):
|
| Returns a code object.
|
| """
|
| c = Code()
|
| - (c.Append()
|
| - .Sblock('class API_%s {' % self._namespace.unix_name)
|
| + (c.Sblock('class API_%s {' % self._namespace.unix_name)
|
| .Append('/*')
|
| .Append(' * API connection')
|
| .Append(' */')
|
| @@ -425,12 +471,13 @@ class _Generator(object):
|
| .Append(' */')
|
| )
|
| for function in self._namespace.functions.values():
|
| - c.Concat(self._GenerateFunction(function))
|
| + # Check for custom dart for this whole property.
|
| + override = self._GetOverride([function.name], document_with=function)
|
| + c.Cblock(override if override is not None
|
| + else self._GenerateFunction(function))
|
|
|
| # Add the constructor.
|
| - (c.Append()
|
| - .Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name)
|
| - )
|
| + c.Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name)
|
|
|
| # Add events to constructor.
|
| for event_name in self._namespace.events:
|
| @@ -459,22 +506,17 @@ class _Generator(object):
|
| 'name': prop.simple_name
|
| }
|
|
|
| - def _GenerateFunctionSignature(self, function):
|
| + def _GenerateFunctionSignature(self, function, convert_optional=False):
|
| """Given a function object, returns the signature for that function.
|
| Recursively generates the signature for callbacks.
|
| Returns a String for the given function.
|
|
|
| - If prepend_this is True, adds "this." to the function's name.
|
| + If convert_optional is True, changes optional parameters to be required.
|
|
|
| e.g.
|
| void onClosed()
|
| bool isOpen([String type])
|
| void doSomething(bool x, void callback([String x]))
|
| -
|
| - e.g. If prepend_this is True:
|
| - void this.onClosed()
|
| - bool this.isOpen([String type])
|
| - void this.doSomething(bool x, void callback([String x]))
|
| """
|
| sig = '%(return_type)s %(name)s(%(params)s)'
|
|
|
| @@ -487,7 +529,8 @@ class _Generator(object):
|
| 'return_type': return_type,
|
| 'name': function.simple_name,
|
| 'params': self._GenerateParameterList(function.params,
|
| - function.callback)
|
| + function.callback,
|
| + convert_optional=convert_optional)
|
| }
|
|
|
| def _GenerateParameterList(self,
|
| @@ -518,7 +561,7 @@ class _Generator(object):
|
|
|
| # Add the callback, if it exists.
|
| if callback:
|
| - c_sig = self._GenerateFunctionSignature(callback)
|
| + c_sig = self._GenerateFunctionSignature(callback, convert_optional=True)
|
| if callback.optional:
|
| params_opt.append(c_sig)
|
| else:
|
| @@ -544,28 +587,35 @@ class _Generator(object):
|
| # prevents a leading comma, e.g. '(, [a, b])'.
|
| return ', '.join(p for p in param_sets if p)
|
|
|
| - def _ConcatOverride(self, c, type_, prop, key_suffix='', add_doc=False):
|
| - """Given a particular type and property to find in the custom dart
|
| - overrides, checks whether there is an override for that key.
|
| - If there is, appends the override code, and returns True.
|
| - If not, returns False.
|
| -
|
| - |key_suffix| will be added to the end of the key before searching, e.g.
|
| - '.set' or '.get' can be used for setters and getters respectively.
|
| + def _GetOverride(self, key_chain, document_with=None):
|
| + """Given a list of keys, joins them with periods and searches for them in
|
| + the custom dart overrides.
|
| + If there is an override for that key, finds the override code and returns
|
| + the Code object. If not, returns None.
|
|
|
| - If add_doc is given, adds the documentation for this property before the
|
| - override code.
|
| + If document_with is not None, adds the documentation for this property
|
| + before the override code.
|
| """
|
| - contents = self._type_overrides.get('%s.%s%s' % (type_.name, prop.name,
|
| - key_suffix))
|
| + c = Code()
|
| + contents = self._type_overrides.get('.'.join(key_chain))
|
| if contents is None:
|
| - return False
|
| + return None
|
|
|
| - if prop is not None:
|
| - c.Concat(self._GenerateDocumentation(prop))
|
| - for line in contents.split('\n'):
|
| + if document_with is not None:
|
| + c.Concat(self._GenerateDocumentation(document_with))
|
| + for line in contents.strip('\n').split('\n'):
|
| c.Append(line)
|
| - return True
|
| + return c
|
| +
|
| + def _AddPrefix(self, name):
|
| + """Given the name of a type, prefixes the namespace (as camelcase) and
|
| + return the new name.
|
| + """
|
| + # TODO(sashab): Split the dart library into multiple files, avoiding the
|
| + # need for this prefixing.
|
| + return ('%s%s' % (
|
| + ''.join(s.capitalize() for s in self._namespace.name.split('.')),
|
| + name))
|
|
|
| def _IsFunction(self, type_):
|
| """Given a model.Type, returns whether this type is a function.
|
| @@ -575,10 +625,16 @@ class _Generator(object):
|
| def _IsSerializableObjectType(self, type_):
|
| """Given a model.Type, returns whether this type is a serializable object.
|
| Serializable objects are custom types defined in this namespace.
|
| +
|
| + If this object is a reference to something not in this namespace, assumes
|
| + its a serializable object.
|
| """
|
| - if (type_.property_type == PropertyType.REF
|
| - and type_.ref_type in self._types):
|
| - return self._IsObjectType(self._types[type_.ref_type])
|
| + if type_.property_type is PropertyType.CHOICES:
|
| + return all(self._IsSerializableObjectType(c) for c in type_.choices)
|
| + if type_.property_type is PropertyType.REF:
|
| + if type_.ref_type in self._types:
|
| + return self._IsObjectType(self._types[type_.ref_type])
|
| + return True
|
| if (type_.property_type == PropertyType.OBJECT
|
| and type_.instance_of in self._types):
|
| return self._IsObjectType(self._types[type_.instance_of])
|
| @@ -592,24 +648,46 @@ class _Generator(object):
|
|
|
| def _IsListOfSerializableObjects(self, type_):
|
| """Given a model.Type, returns whether this type is a list of serializable
|
| - objects (PropertyType.REF types).
|
| + objects (or regular objects, if this list is treated as a type - in this
|
| + case, the item type was defined inline).
|
| +
|
| + If this type is a reference to something not in this namespace, assumes
|
| + it is not a list of serializable objects.
|
| """
|
| + if type_.property_type is PropertyType.CHOICES:
|
| + return all(self._IsListOfSerializableObjects(c) for c in type_.choices)
|
| + if type_.property_type is PropertyType.REF:
|
| + if type_.ref_type in self._types:
|
| + return self._IsListOfSerializableObjects(self._types[type_.ref_type])
|
| + return False
|
| return (type_.property_type is PropertyType.ARRAY and
|
| - type_.item_type.property_type is PropertyType.REF)
|
| + (self._IsSerializableObjectType(type_.item_type)))
|
|
|
| def _IsListOfBaseTypes(self, type_):
|
| """Given a model.Type, returns whether this type is a list of base type
|
| objects (PropertyType.REF types).
|
| """
|
| + if type_.property_type is PropertyType.CHOICES:
|
| + return all(self._IsListOfBaseTypes(c) for c in type_.choices)
|
| return (type_.property_type is PropertyType.ARRAY and
|
| self._IsBaseType(type_.item_type))
|
|
|
| def _IsBaseType(self, type_):
|
| """Given a model.type_, returns whether this type is a base type
|
| - (string, number or boolean).
|
| + (string, number, boolean, or a list of these).
|
| +
|
| + If type_ is a Choices object, returns True if all possible choices are base
|
| + types.
|
| """
|
| - return (self._GetDartType(type_) in
|
| - ['bool', 'num', 'int', 'double', 'String'])
|
| + # TODO(sashab): Remove 'Choices' as a base type once they are wrapped in
|
| + # native Dart classes.
|
| + if type_.property_type is PropertyType.CHOICES:
|
| + return all(self._IsBaseType(c) for c in type_.choices)
|
| + return (
|
| + (self._GetDartType(type_) in ['bool', 'num', 'int', 'double', 'String'])
|
| + or (type_.property_type is PropertyType.ARRAY
|
| + and self._IsBaseType(type_.item_type))
|
| + )
|
|
|
| def _GetDartType(self, type_):
|
| """Given a model.Type object, returns its type as a Dart string.
|
| @@ -619,6 +697,8 @@ class _Generator(object):
|
|
|
| prop_type = type_.property_type
|
| if prop_type is PropertyType.REF:
|
| + if type_.ref_type in self._types:
|
| + return self._GetDartType(self._types[type_.ref_type])
|
| # TODO(sashab): If the type is foreign, it might have to be imported.
|
| return StripNamespace(type_.ref_type)
|
| elif prop_type is PropertyType.BOOLEAN:
|
| @@ -634,12 +714,20 @@ class _Generator(object):
|
| elif prop_type is PropertyType.ENUM:
|
| return 'String'
|
| elif prop_type is PropertyType.CHOICES:
|
| - # TODO: What is a Choices type? Is it closer to a Map Dart object?
|
| + # TODO(sashab): Think of a nice way to generate code for Choices objects
|
| + # in Dart.
|
| return 'Object'
|
| elif prop_type is PropertyType.ANY:
|
| return 'Object'
|
| elif prop_type is PropertyType.OBJECT:
|
| - return type_.instance_of or 'Object'
|
| + # TODO(sashab): type_.name is the name of the function's parameter for
|
| + # inline types defined in functions. Think of a way to generate names
|
| + # for this, or remove all inline type definitions at the start.
|
| + if type_.instance_of is not None:
|
| + return type_.instance_of
|
| + if not isinstance(type_.parent, Function):
|
| + return self._AddPrefix(type_.name)
|
| + return 'Object'
|
| elif prop_type is PropertyType.FUNCTION:
|
| return 'Function'
|
| elif prop_type is PropertyType.ARRAY:
|
|
|