Home | History | Annotate | Download | only in json_schema_compiler
      1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 """
      5 Generator language component for compiler.py that adds Dart language support.
      6 """
      7 
      8 from code import Code
      9 from model import Function, PropertyType
     10 from schema_util import StripNamespace
     11 
     12 import os
     13 from datetime import datetime
     14 
     15 LICENSE = (
     16 """// Copyright (c) %s, the Dart project authors.  Please see the AUTHORS file
     17 // for details. All rights reserved. Use of this source code is governed by a
     18 // BSD-style license that can be found in the LICENSE file.""" %
     19     datetime.now().year)
     20 
     21 class DartGenerator(object):
     22   def __init__(self, dart_overrides_dir=None):
     23     self._dart_overrides_dir = dart_overrides_dir
     24 
     25   def Generate(self, namespace):
     26     return _Generator(namespace, self._dart_overrides_dir).Generate()
     27 
     28 
     29 class _Generator(object):
     30   """A .dart generator for a namespace.
     31   """
     32 
     33   def __init__(self, namespace, dart_overrides_dir=None):
     34     self._namespace = namespace
     35     # TODO(sashab): Once inline type definitions start being added to
     36     # self._types, make a _FindType(self, type_) function that looks at
     37     # self._namespace.types.
     38     self._types = namespace.types
     39 
     40     # Build a dictionary of Type Name --> Custom Dart code.
     41     self._type_overrides = {}
     42     if dart_overrides_dir is not None:
     43       for filename in os.listdir(dart_overrides_dir):
     44         if filename.startswith(namespace.unix_name):
     45           with open(os.path.join(dart_overrides_dir, filename)) as f:
     46             # Split off the namespace and file extension, leaving just the type.
     47             type_path = '.'.join(filename.split('.')[1:-1])
     48             self._type_overrides[type_path] = f.read()
     49 
     50     # TODO(sashab): Add all inline type definitions to the global Types
     51     # dictionary here, so they have proper names, and are implemented along with
     52     # all other types. Also update the parameters/members with these types
     53     # to reference these new types instead.
     54 
     55   def Generate(self):
     56     """Generates a Code object with the .dart for the entire namespace.
     57     """
     58     c = Code()
     59     (c.Append(LICENSE)
     60       .Append()
     61       .Append('// Generated from namespace: %s' % self._namespace.name)
     62       .Append()
     63       .Append('part of chrome;'))
     64 
     65     if self._types:
     66       (c.Append()
     67         .Append('/**')
     68         .Append(' * Types')
     69         .Append(' */')
     70         .Append()
     71       )
     72     for type_ in self._types.values():
     73       # Check for custom dart for this whole type.
     74       override = self._GetOverride([type_.name], document_with=type_)
     75       c.Cblock(override if override is not None else self._GenerateType(type_))
     76 
     77     if self._namespace.events:
     78       (c.Append('/**')
     79         .Append(' * Events')
     80         .Append(' */')
     81         .Append()
     82       )
     83     for event_name in self._namespace.events:
     84       c.Cblock(self._GenerateEvent(self._namespace.events[event_name]))
     85 
     86     (c.Append('/**')
     87       .Append(' * Functions')
     88       .Append(' */')
     89       .Append()
     90     )
     91     c.Cblock(self._GenerateMainClass())
     92 
     93     return c
     94 
     95   def _GenerateType(self, type_):
     96     """Given a Type object, returns the Code with the .dart for this
     97     type's definition.
     98 
     99     Assumes this type is a Parameter Type (creatable by user), and creates an
    100     object that extends ChromeObject. All parameters are specifiable as named
    101     arguments in the constructor, and all methods are wrapped with getters and
    102     setters that hide the JS() implementation.
    103     """
    104     c = Code()
    105 
    106     # Since enums are just treated as strings for now, don't generate their
    107     # type.
    108     # TODO(sashab): Find a nice way to wrap enum objects.
    109     if type_.property_type is PropertyType.ENUM:
    110       return c
    111 
    112     (c.Concat(self._GenerateDocumentation(type_))
    113       .Sblock('class %(type_name)s extends ChromeObject {')
    114     )
    115 
    116     # Check whether this type has function members. If it does, don't allow
    117     # public construction.
    118     add_public_constructor = all(not self._IsFunction(p.type_)
    119                                  for p in type_.properties.values())
    120     constructor_fields = [self._GeneratePropertySignature(p)
    121                           for p in type_.properties.values()]
    122 
    123     if add_public_constructor:
    124       (c.Append('/*')
    125         .Append(' * Public constructor')
    126         .Append(' */')
    127         .Sblock('%(type_name)s({%(constructor_fields)s}) {')
    128       )
    129 
    130       for prop_name in type_.properties:
    131         (c.Sblock('if (%s != null)' % prop_name)
    132           .Append('this.%s = %s;' % (prop_name, prop_name))
    133           .Eblock()
    134         )
    135       (c.Eblock('}')
    136         .Append()
    137       )
    138 
    139     (c.Append('/*')
    140       .Append(' * Private constructor')
    141       .Append(' */')
    142       .Append('%(type_name)s._proxy(_jsObject) : super._proxy(_jsObject);')
    143     )
    144 
    145     # Add an accessor (getter & setter) for each property.
    146     properties = [p for p in type_.properties.values()
    147                   if not self._IsFunction(p.type_)]
    148     if properties:
    149       (c.Append()
    150         .Append('/*')
    151         .Append(' * Public accessors')
    152         .Append(' */')
    153       )
    154     for prop in properties:
    155       override = self._GetOverride([type_.name, prop.name], document_with=prop)
    156       c.Concat(override if override is not None
    157                else self._GenerateGetterAndSetter(type_, prop))
    158 
    159     # Now add all the methods.
    160     methods = [t for t in type_.properties.values()
    161                if self._IsFunction(t.type_)]
    162     if methods:
    163       (c.Append()
    164         .Append('/*')
    165         .Append(' * Methods')
    166         .Append(' */')
    167       )
    168     for prop in methods:
    169       # Check if there's an override for this method.
    170       override = self._GetOverride([type_.name, prop.name], document_with=prop)
    171       c.Cblock(override if override is not None
    172                else self._GenerateFunction(prop.type_.function))
    173 
    174     (c.Eblock('}')
    175       .Substitute({
    176         'type_name': self._AddPrefix(type_.simple_name),
    177         'constructor_fields': ', '.join(constructor_fields)
    178       })
    179     )
    180 
    181     return c
    182 
    183   def _GenerateGetterAndSetter(self, type_, prop):
    184     """Given a Type and Property, returns the Code object for the getter and
    185     setter for that property.
    186     """
    187     c = Code()
    188     override = self._GetOverride([type_.name, prop.name, '.get'],
    189                                  document_with=prop)
    190     c.Cblock(override if override is not None
    191              else self._GenerateGetter(type_, prop))
    192     override = self._GetOverride([type_.name, prop.name, '.set'])
    193     c.Cblock(override if override is not None
    194              else self._GenerateSetter(type_, prop))
    195     return c
    196 
    197   def _GenerateGetter(self, type_, prop):
    198     """Given a Type and Property, returns the Code object for the getter for
    199     that property.
    200 
    201     Also adds the documentation for this property before the method.
    202     """
    203     c = Code()
    204     c.Concat(self._GenerateDocumentation(prop))
    205 
    206     type_name = self._GetDartType(prop.type_)
    207     if (self._IsBaseType(prop.type_)):
    208       c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
    209           (type_name, prop.name, type_name, prop.name))
    210     elif self._IsSerializableObjectType(prop.type_):
    211       c.Append("%s get %s => new %s._proxy(JS('', '#.%s', "
    212                "this._jsObject));" %
    213           (type_name, prop.name, type_name, prop.name))
    214     elif self._IsListOfSerializableObjects(prop.type_):
    215       (c.Sblock('%s get %s {' % (type_name, prop.name))
    216         .Append('%s __proxy_%s = new %s();' % (type_name, prop.name,
    217                                                type_name))
    218         .Append("int count = JS('int', '#.%s.length', this._jsObject);" %
    219             prop.name)
    220         .Sblock("for (int i = 0; i < count; i++) {")
    221         .Append("var item = JS('', '#.%s[#]', this._jsObject, i);" % prop.name)
    222         .Append('__proxy_%s.add(new %s._proxy(item));' % (prop.name,
    223             self._GetDartType(prop.type_.item_type)))
    224         .Eblock('}')
    225         .Append('return __proxy_%s;' % prop.name)
    226         .Eblock('}')
    227       )
    228     elif self._IsObjectType(prop.type_):
    229       # TODO(sashab): Think of a way to serialize generic Dart objects.
    230       if type_name in self._types:
    231         c.Append("%s get %s => new %s._proxy(JS('%s', '#.%s', "
    232                  "this._jsObject));" %
    233             (type_name, prop.name, type_name, type_name, prop.name))
    234       else:
    235         c.Append("%s get %s => JS('%s', '#.%s', this._jsObject);" %
    236             (type_name, prop.name, type_name, prop.name))
    237     else:
    238       raise Exception(
    239           "Could not generate wrapper for %s.%s: unserializable type %s" %
    240           (type_.name, prop.name, type_name)
    241       )
    242     return c
    243 
    244   def _GenerateSetter(self, type_, prop):
    245     """Given a Type and Property, returns the Code object for the setter for
    246     that property.
    247     """
    248     c = Code()
    249     type_name = self._GetDartType(prop.type_)
    250     wrapped_name = prop.name
    251     if not self._IsBaseType(prop.type_):
    252       wrapped_name = 'convertArgument(%s)' % prop.name
    253 
    254     (c.Sblock("void set %s(%s %s) {" % (prop.name, type_name, prop.name))
    255       .Append("JS('void', '#.%s = #', this._jsObject, %s);" %
    256         (prop.name, wrapped_name))
    257       .Eblock("}")
    258     )
    259     return c
    260 
    261   def _GenerateDocumentation(self, prop):
    262     """Given an object, generates the documentation for this object (as a
    263     code string) and returns the Code object.
    264 
    265     Returns an empty code object if the object has no documentation.
    266 
    267     Uses triple-quotes for the string.
    268     """
    269     c = Code()
    270     if prop.description is not None:
    271       for line in prop.description.split('\n'):
    272         c.Comment(line, comment_prefix='/// ')
    273     return c
    274 
    275   def _GenerateFunction(self, f):
    276     """Returns the Code object for the given function.
    277     """
    278     c = Code()
    279     c.Concat(self._GenerateDocumentation(f))
    280 
    281     if not self._NeedsProxiedCallback(f):
    282       c.Append("%s => %s;" % (self._GenerateFunctionSignature(f),
    283                               self._GenerateProxyCall(f)))
    284       return c
    285 
    286     (c.Sblock("%s {" % self._GenerateFunctionSignature(f))
    287       .Concat(self._GenerateProxiedFunction(f.callback, f.callback.name))
    288       .Append('%s;' % self._GenerateProxyCall(f))
    289       .Eblock('}')
    290     )
    291 
    292     return c
    293 
    294   def _GenerateProxiedFunction(self, f, callback_name):
    295     """Given a function (assumed to be a callback), generates the proxied
    296     version of this function, which calls |callback_name| if it is defined.
    297 
    298     Returns a Code object.
    299     """
    300     c = Code()
    301     proxied_params = []
    302     # A list of Properties, containing List<*> objects that need proxying for
    303     # their members (by copying out each member and proxying it).
    304     lists_to_proxy = []
    305     for p in f.params:
    306       if self._IsBaseType(p.type_):
    307         proxied_params.append(p.name)
    308       elif self._IsSerializableObjectType(p.type_):
    309         proxied_params.append('new %s._proxy(%s)' % (
    310             self._GetDartType(p.type_), p.name))
    311       elif self._IsListOfSerializableObjects(p.type_):
    312         proxied_params.append('__proxy_%s' % p.name)
    313         lists_to_proxy.append(p)
    314       elif self._IsObjectType(p.type_):
    315         # TODO(sashab): Find a way to build generic JS objects back in Dart.
    316         proxied_params.append('%s' % p.name)
    317       elif p.type_.property_type is PropertyType.ARRAY:
    318         # TODO(sashab): This might be okay - what if this is a list of
    319         # FileEntry elements? In this case, a basic list will proxy the objects
    320         # fine.
    321         proxied_params.append('%s' % p.name)
    322       else:
    323         raise Exception(
    324             "Cannot automatically create proxy; can't wrap %s, type %s" % (
    325                 self._GenerateFunctionSignature(f), self._GetDartType(p.type_)))
    326 
    327     (c.Sblock("void __proxy_callback(%s) {" % ', '.join(p.name for p in
    328                                                f.params))
    329       .Sblock('if (%s != null) {' % callback_name)
    330     )
    331 
    332     # Add the proxied lists.
    333     for list_to_proxy in lists_to_proxy:
    334       (c.Append("%s __proxy_%s = new %s();" % (
    335                     self._GetDartType(list_to_proxy.type_),
    336                     list_to_proxy.name,
    337                     self._GetDartType(list_to_proxy.type_)))
    338         .Sblock("for (var o in %s) {" % list_to_proxy.name)
    339         .Append('__proxy_%s.add(new %s._proxy(o));' % (list_to_proxy.name,
    340             self._GetDartType(list_to_proxy.type_.item_type)))
    341         .Eblock("}")
    342       )
    343 
    344     (c.Append("%s(%s);" % (callback_name, ', '.join(proxied_params)))
    345       .Eblock('}')
    346       .Eblock('}')
    347     )
    348     return c
    349 
    350   def _NeedsProxiedCallback(self, f):
    351     """Given a function, returns True if this function's callback needs to be
    352     proxied, False if not.
    353 
    354     Function callbacks need to be proxied if they have at least one
    355     non-base-type parameter.
    356     """
    357     return f.callback and self._NeedsProxy(f.callback)
    358 
    359   def _NeedsProxy(self, f):
    360     """Given a function, returns True if it needs to be proxied, False if not.
    361 
    362     A function needs to be proxied if any of its members are non-base types.
    363     This means that, when the function object is passed to Javascript, it
    364     needs to be wrapped in a "proxied" call that converts the JS inputs to Dart
    365     objects explicitly, before calling the real function with these new objects.
    366     """
    367     return any(not self._IsBaseType(p.type_) for p in f.params)
    368 
    369   def _GenerateProxyCall(self, function, call_target='this._jsObject'):
    370     """Given a function, generates the code to call that function via JS().
    371     Returns a string.
    372 
    373     |call_target| is the name of the object to call the function on. The default
    374     is this._jsObject.
    375 
    376     e.g.
    377         JS('void', '#.resizeTo(#, #)', this._jsObject, width, height)
    378         JS('void', '#.setBounds(#)', this._jsObject, convertArgument(bounds))
    379     """
    380     n_params = len(function.params)
    381     if function.callback:
    382       n_params += 1
    383 
    384     return_type_str = self._GetDartType(function.returns)
    385     params = []
    386 
    387     # If this object is serializable, don't convert the type from JS - pass the
    388     # JS object straight into the proxy.
    389     if self._IsSerializableObjectType(function.returns):
    390       params.append("''")
    391     else:
    392       params.append("'%s'" % return_type_str)
    393 
    394     params.append("'#.%s(%s)'" % (function.name, ', '.join(['#'] * n_params)))
    395     params.append(call_target)
    396 
    397     for param in function.params:
    398       if not self._IsBaseType(param.type_):
    399         params.append('convertArgument(%s)' % param.name)
    400       else:
    401         params.append(param.name)
    402     if function.callback:
    403       # If this isn't a base type, we need a proxied callback.
    404       callback_name = function.callback.name
    405       if self._NeedsProxiedCallback(function):
    406         callback_name = "__proxy_callback"
    407       params.append('convertDartClosureToJS(%s, %s)' % (callback_name,
    408                     len(function.callback.params)))
    409 
    410     # If the object is serializable, call the proxy constructor for this type.
    411     proxy_call = 'JS(%s)' % ', '.join(params)
    412     if self._IsSerializableObjectType(function.returns):
    413       proxy_call = 'new %s._proxy(%s)' % (return_type_str, proxy_call)
    414 
    415     return proxy_call
    416 
    417   def _GenerateEvent(self, event):
    418     """Given a Function object, returns the Code with the .dart for this event,
    419     represented by the function.
    420 
    421     All events extend the Event base type.
    422     """
    423     c = Code()
    424 
    425     # Add documentation for this event.
    426     (c.Concat(self._GenerateDocumentation(event))
    427       .Sblock('class Event_%(event_name)s extends Event {')
    428     )
    429 
    430     # If this event needs a proxy, all calls need to be proxied.
    431     needs_proxy = self._NeedsProxy(event)
    432 
    433     # Override Event callback type definitions.
    434     for ret_type, event_func in (('void', 'addListener'),
    435                                  ('void', 'removeListener'),
    436                                  ('bool', 'hasListener')):
    437       param_list = self._GenerateParameterList(event.params, event.callback,
    438                                                convert_optional=True)
    439       if needs_proxy:
    440         (c.Sblock('%s %s(void callback(%s)) {' % (ret_type, event_func,
    441                                                  param_list))
    442           .Concat(self._GenerateProxiedFunction(event, 'callback'))
    443           .Append('super.%s(__proxy_callback);' % event_func)
    444           .Eblock('}')
    445         )
    446       else:
    447         c.Append('%s %s(void callback(%s)) => super.%s(callback);' %
    448             (ret_type, event_func, param_list, event_func))
    449       c.Append()
    450 
    451     # Generate the constructor.
    452     (c.Append('Event_%(event_name)s(jsObject) : '
    453               'super._(jsObject, %(param_num)d);')
    454       .Eblock('}')
    455       .Substitute({
    456         'event_name': self._namespace.unix_name + '_' + event.name,
    457         'param_num': len(event.params)
    458       })
    459     )
    460 
    461     return c
    462 
    463   def _GenerateMainClass(self):
    464     """Generates the main class for this file, which links to all functions
    465     and events.
    466 
    467     Returns a code object.
    468     """
    469     c = Code()
    470     (c.Sblock('class API_%s {' % self._namespace.unix_name)
    471       .Append('/*')
    472       .Append(' * API connection')
    473       .Append(' */')
    474       .Append('Object _jsObject;')
    475     )
    476 
    477     # Add events.
    478     if self._namespace.events:
    479       (c.Append()
    480         .Append('/*')
    481         .Append(' * Events')
    482         .Append(' */')
    483       )
    484     for event_name in self._namespace.events:
    485       c.Append('Event_%s_%s %s;' % (self._namespace.unix_name, event_name,
    486                                     event_name))
    487 
    488     # Add functions.
    489     if self._namespace.functions:
    490       (c.Append()
    491         .Append('/*')
    492         .Append(' * Functions')
    493         .Append(' */')
    494       )
    495     for function in self._namespace.functions.values():
    496       # Check for custom dart for this whole property.
    497       override = self._GetOverride([function.name], document_with=function)
    498       c.Cblock(override if override is not None
    499                else self._GenerateFunction(function))
    500 
    501     # Add the constructor.
    502     c.Sblock('API_%s(this._jsObject) {' % self._namespace.unix_name)
    503 
    504     # Add events to constructor.
    505     for event_name in self._namespace.events:
    506       c.Append("%s = new Event_%s_%s(JS('', '#.%s', this._jsObject));" %
    507         (event_name, self._namespace.unix_name, event_name, event_name))
    508 
    509     (c.Eblock('}')
    510       .Eblock('}')
    511     )
    512     return c
    513 
    514   def _GeneratePropertySignature(self, prop):
    515     """Given a property, returns a signature for that property.
    516     Recursively generates the signature for callbacks.
    517     Returns a String for the given property.
    518 
    519     e.g.
    520       bool x
    521       void onClosed()
    522       void doSomething(bool x, void callback([String x]))
    523     """
    524     if self._IsFunction(prop.type_):
    525       return self._GenerateFunctionSignature(prop.type_.function)
    526     return '%(type)s %(name)s' % {
    527                'type': self._GetDartType(prop.type_),
    528                'name': prop.simple_name
    529            }
    530 
    531   def _GenerateFunctionSignature(self, function, convert_optional=False):
    532     """Given a function object, returns the signature for that function.
    533     Recursively generates the signature for callbacks.
    534     Returns a String for the given function.
    535 
    536     If convert_optional is True, changes optional parameters to be required.
    537 
    538     e.g.
    539       void onClosed()
    540       bool isOpen([String type])
    541       void doSomething(bool x, void callback([String x]))
    542     """
    543     sig = '%(return_type)s %(name)s(%(params)s)'
    544 
    545     if function.returns:
    546       return_type = self._GetDartType(function.returns)
    547     else:
    548       return_type = 'void'
    549 
    550     return sig % {
    551         'return_type': return_type,
    552         'name': function.simple_name,
    553         'params': self._GenerateParameterList(function.params,
    554                                               function.callback,
    555                                               convert_optional=convert_optional)
    556     }
    557 
    558   def _GenerateParameterList(self,
    559                              params,
    560                              callback=None,
    561                              convert_optional=False):
    562     """Given a list of function parameters, generates their signature (as a
    563     string).
    564 
    565     e.g.
    566       [String type]
    567       bool x, void callback([String x])
    568 
    569     If convert_optional is True, changes optional parameters to be required.
    570     Useful for callbacks, where optional parameters are treated as required.
    571     """
    572     # Params lists (required & optional), to be joined with commas.
    573     # TODO(sashab): Don't assume optional params always come after required
    574     # ones.
    575     params_req = []
    576     params_opt = []
    577     for param in params:
    578       p_sig = self._GeneratePropertySignature(param)
    579       if param.optional and not convert_optional:
    580         params_opt.append(p_sig)
    581       else:
    582         params_req.append(p_sig)
    583 
    584     # Add the callback, if it exists.
    585     if callback:
    586       c_sig = self._GenerateFunctionSignature(callback, convert_optional=True)
    587       if callback.optional:
    588         params_opt.append(c_sig)
    589       else:
    590         params_req.append(c_sig)
    591 
    592     # Join the parameters with commas.
    593     # Optional parameters have to be in square brackets, e.g.:
    594     #
    595     #  required params | optional params |     output
    596     #        []        |        []       |       ''
    597     #      [x, y]      |        []       |     'x, y'
    598     #        []        |      [a, b]     |    '[a, b]'
    599     #      [x, y]      |      [a, b]     | 'x, y, [a, b]'
    600     if params_opt:
    601       params_opt[0] = '[%s' % params_opt[0]
    602       params_opt[-1] = '%s]' % params_opt[-1]
    603     param_sets = [', '.join(params_req), ', '.join(params_opt)]
    604 
    605     # The 'if p' part here is needed to prevent commas where there are no
    606     # parameters of a certain type.
    607     # If there are no optional parameters, this prevents a _trailing_ comma,
    608     # e.g. '(x, y,)'. Similarly, if there are no required parameters, this
    609     # prevents a leading comma, e.g. '(, [a, b])'.
    610     return ', '.join(p for p in param_sets if p)
    611 
    612   def _GetOverride(self, key_chain, document_with=None):
    613     """Given a list of keys, joins them with periods and searches for them in
    614     the custom dart overrides.
    615     If there is an override for that key, finds the override code and returns
    616     the Code object. If not, returns None.
    617 
    618     If document_with is not None, adds the documentation for this property
    619     before the override code.
    620     """
    621     c = Code()
    622     contents = self._type_overrides.get('.'.join(key_chain))
    623     if contents is None:
    624       return None
    625 
    626     if document_with is not None:
    627       c.Concat(self._GenerateDocumentation(document_with))
    628     for line in contents.strip('\n').split('\n'):
    629       c.Append(line)
    630     return c
    631 
    632   def _AddPrefix(self, name):
    633     """Given the name of a type, prefixes the namespace (as camelcase) and
    634     returns the new name.
    635     """
    636     # TODO(sashab): Split the dart library into multiple files, avoiding the
    637     # need for this prefixing.
    638     return ('%s%s' % (
    639         ''.join(s.capitalize() for s in self._namespace.name.split('.')),
    640         name))
    641 
    642   def _IsFunction(self, type_):
    643     """Given a model.Type, returns whether this type is a function.
    644     """
    645     return type_.property_type == PropertyType.FUNCTION
    646 
    647   def _IsSerializableObjectType(self, type_):
    648     """Given a model.Type, returns whether this type is a serializable object.
    649     Serializable objects are custom types defined in this namespace.
    650 
    651     If this object is a reference to something not in this namespace, assumes
    652     its a serializable object.
    653     """
    654     if type_ is None:
    655       return False
    656     if type_.property_type is PropertyType.CHOICES:
    657       return all(self._IsSerializableObjectType(c) for c in type_.choices)
    658     if type_.property_type is PropertyType.REF:
    659       if type_.ref_type in self._types:
    660         return self._IsObjectType(self._types[type_.ref_type])
    661       return True
    662     if (type_.property_type == PropertyType.OBJECT
    663         and type_.instance_of in self._types):
    664       return self._IsObjectType(self._types[type_.instance_of])
    665     return False
    666 
    667   def _IsObjectType(self, type_):
    668     """Given a model.Type, returns whether this type is an object.
    669     """
    670     return (self._IsSerializableObjectType(type_)
    671             or type_.property_type in [PropertyType.OBJECT, PropertyType.ANY])
    672 
    673   def _IsListOfSerializableObjects(self, type_):
    674     """Given a model.Type, returns whether this type is a list of serializable
    675     objects (or regular objects, if this list is treated as a type - in this
    676     case, the item type was defined inline).
    677 
    678     If this type is a reference to something not in this namespace, assumes
    679     it is not a list of serializable objects.
    680     """
    681     if type_.property_type is PropertyType.CHOICES:
    682       return all(self._IsListOfSerializableObjects(c) for c in type_.choices)
    683     if type_.property_type is PropertyType.REF:
    684       if type_.ref_type in self._types:
    685         return self._IsListOfSerializableObjects(self._types[type_.ref_type])
    686       return False
    687     return (type_.property_type is PropertyType.ARRAY and
    688         (self._IsSerializableObjectType(type_.item_type)))
    689 
    690   def _IsListOfBaseTypes(self, type_):
    691     """Given a model.Type, returns whether this type is a list of base type
    692     objects (PropertyType.REF types).
    693     """
    694     if type_.property_type is PropertyType.CHOICES:
    695       return all(self._IsListOfBaseTypes(c) for c in type_.choices)
    696     return (type_.property_type is PropertyType.ARRAY and
    697             self._IsBaseType(type_.item_type))
    698 
    699   def _IsBaseType(self, type_):
    700     """Given a model.type_, returns whether this type is a base type
    701     (string, number, boolean, or a list of these).
    702 
    703     If type_ is a Choices object, returns True if all possible choices are base
    704     types.
    705     """
    706     # TODO(sashab): Remove 'Choices' as a base type once they are wrapped in
    707     # native Dart classes.
    708     if type_.property_type is PropertyType.CHOICES:
    709       return all(self._IsBaseType(c) for c in type_.choices)
    710     return (
    711         (self._GetDartType(type_) in ['bool', 'num', 'int', 'double', 'String'])
    712         or (type_.property_type is PropertyType.ARRAY
    713             and self._IsBaseType(type_.item_type))
    714     )
    715 
    716   def _GetDartType(self, type_):
    717     """Given a model.Type object, returns its type as a Dart string.
    718     """
    719     if type_ is None:
    720       return 'void'
    721 
    722     prop_type = type_.property_type
    723     if prop_type is PropertyType.REF:
    724       if type_.ref_type in self._types:
    725         return self._GetDartType(self._types[type_.ref_type])
    726       # TODO(sashab): If the type is foreign, it might have to be imported.
    727       return StripNamespace(type_.ref_type)
    728     elif prop_type is PropertyType.BOOLEAN:
    729       return 'bool'
    730     elif prop_type is PropertyType.INTEGER:
    731       return 'int'
    732     elif prop_type is PropertyType.INT64:
    733       return 'num'
    734     elif prop_type is PropertyType.DOUBLE:
    735       return 'double'
    736     elif prop_type is PropertyType.STRING:
    737       return 'String'
    738     elif prop_type is PropertyType.ENUM:
    739       return 'String'
    740     elif prop_type is PropertyType.CHOICES:
    741       # TODO(sashab): Think of a nice way to generate code for Choices objects
    742       # in Dart.
    743       return 'Object'
    744     elif prop_type is PropertyType.ANY:
    745       return 'Object'
    746     elif prop_type is PropertyType.OBJECT:
    747       # TODO(sashab): type_.name is the name of the function's parameter for
    748       # inline types defined in functions. Think of a way to generate names
    749       # for this, or remove all inline type definitions at the start.
    750       if type_.instance_of is not None:
    751         return type_.instance_of
    752       if not isinstance(type_.parent, Function):
    753         return self._AddPrefix(type_.name)
    754       return 'Object'
    755     elif prop_type is PropertyType.FUNCTION:
    756       return 'Function'
    757     elif prop_type is PropertyType.ARRAY:
    758       return 'List<%s>' % self._GetDartType(type_.item_type)
    759     elif prop_type is PropertyType.BINARY:
    760       return 'String'
    761     else:
    762       raise NotImplementedError(prop_type)
    763 
    764