OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 3 # for details. All rights reserved. Use of this source code is governed by a |
| 4 # BSD-style license that can be found in the LICENSE file. |
| 5 |
| 6 """This module provides shared functionality for systems to generate |
| 7 Dart APIs from the IDL database.""" |
| 8 |
| 9 import re |
| 10 |
| 11 # IDL->Dart primitive types conversion. |
| 12 _idl_to_dart_type_conversions = { |
| 13 'any': 'Object', |
| 14 'any[]': 'List', |
| 15 'custom': 'Dynamic', |
| 16 'boolean': 'bool', |
| 17 'DOMObject': 'Object', |
| 18 'DOMString': 'String', |
| 19 'DOMStringList': 'List<String>', |
| 20 'DOMTimeStamp': 'int', |
| 21 'Date': 'Date', |
| 22 # Map to num to enable callers to pass in Dart int, rational |
| 23 # types. Our implementations will need to convert these to |
| 24 # doubles or floats as needed. |
| 25 'double': 'num', |
| 26 'float': 'num', |
| 27 'int': 'int', |
| 28 # Map to extra precision - int is a bignum in Dart. |
| 29 'long': 'int', |
| 30 'long long': 'int', |
| 31 'object': 'Object', |
| 32 # Map to extra precision - int is a bignum in Dart. |
| 33 'short': 'int', |
| 34 'string': 'String', |
| 35 'void': 'void', |
| 36 'Array': 'List', |
| 37 'sequence': 'List', |
| 38 # TODO(sra): Come up with some meaningful name so that where this appears in |
| 39 # the documentation, the user is made aware that only a limited subset of |
| 40 # serializable types are actually permitted. |
| 41 'SerializedScriptValue': 'Dynamic', |
| 42 # TODO(vsm): Automatically recognize types defined in src. |
| 43 'TimeoutHandler': 'TimeoutHandler', |
| 44 'RequestAnimationFrameCallback': 'RequestAnimationFrameCallback', |
| 45 |
| 46 # TODO(sra): Flags is really a dictionary: {create:bool, exclusive:bool} |
| 47 # http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#the-flags-interfa
ce |
| 48 'WebKitFlags': 'Object', |
| 49 } |
| 50 |
| 51 _dart_to_idl_type_conversions = dict((v,k) for k, v in |
| 52 _idl_to_dart_type_conversions.iteritems()) |
| 53 |
| 54 # |
| 55 # Identifiers that are used in the IDL than need to be treated specially because |
| 56 # *some* JavaScript processors forbid them as properties. |
| 57 # |
| 58 _javascript_keywords = ['delete', 'continue'] |
| 59 |
| 60 # |
| 61 # Interface version of the DOM needs to delegate typed array constructors to a |
| 62 # factory provider. |
| 63 # |
| 64 interface_factories = { |
| 65 'Float32Array': '_TypedArrayFactoryProvider', |
| 66 'Float64Array': '_TypedArrayFactoryProvider', |
| 67 'Int8Array': '_TypedArrayFactoryProvider', |
| 68 'Int16Array': '_TypedArrayFactoryProvider', |
| 69 'Int32Array': '_TypedArrayFactoryProvider', |
| 70 'Uint8Array': '_TypedArrayFactoryProvider', |
| 71 'Uint16Array': '_TypedArrayFactoryProvider', |
| 72 'Uint32Array': '_TypedArrayFactoryProvider', |
| 73 'Uint8ClampedArray': '_TypedArrayFactoryProvider', |
| 74 } |
| 75 |
| 76 # |
| 77 # Custom methods that must be implemented by hand. |
| 78 # |
| 79 _custom_methods = set([ |
| 80 ('DOMWindow', 'setInterval'), |
| 81 ('DOMWindow', 'setTimeout'), |
| 82 ('WorkerContext', 'setInterval'), |
| 83 ('WorkerContext', 'setTimeout'), |
| 84 ('CanvasRenderingContext2D', 'setFillStyle'), |
| 85 ('CanvasRenderingContext2D', 'setStrokeStyle'), |
| 86 ('CanvasRenderingContext2D', 'setFillStyle'), |
| 87 ]) |
| 88 |
| 89 # |
| 90 # Custom getters that must be implemented by hand. |
| 91 # |
| 92 _custom_getters = set([ |
| 93 ('DOMWindow', 'localStorage'), |
| 94 ]) |
| 95 |
| 96 # |
| 97 # Custom native specs for the Frog dom. |
| 98 # |
| 99 _frog_dom_custom_native_specs = { |
| 100 # Decorate the singleton Console object, if present (workers do not have a |
| 101 # console). |
| 102 'Console': "=(typeof console == 'undefined' ? {} : console)", |
| 103 |
| 104 # DOMWindow aliased with global scope. |
| 105 'DOMWindow': '@*DOMWindow', |
| 106 } |
| 107 |
| 108 # |
| 109 # Simple method substitution when one method had different names on different |
| 110 # browsers, but are otherwise identical. The alternates are tried in order and |
| 111 # the first one defined is used. |
| 112 # |
| 113 # This can be probably be removed when Chrome renames initWebKitWheelEvent to |
| 114 # initWheelEvent. |
| 115 # |
| 116 _alternate_methods = { |
| 117 ('WheelEvent', 'initWheelEvent'): ['initWebKitWheelEvent', 'initWheelEvent'] |
| 118 } |
| 119 |
| 120 def ConvertPrimitiveType(type_name): |
| 121 if type_name.startswith('unsigned '): |
| 122 type_name = type_name[len('unsigned '):] |
| 123 |
| 124 if type_name in _idl_to_dart_type_conversions: |
| 125 # Primitive type conversion |
| 126 return _idl_to_dart_type_conversions[type_name] |
| 127 return None |
| 128 |
| 129 def IsPrimitiveType(type_name): |
| 130 return (ConvertPrimitiveType(type_name) is not None or |
| 131 type_name in _dart_to_idl_type_conversions) |
| 132 |
| 133 def MaybeListElementTypeName(type_name): |
| 134 """Returns the List element type T from string of form "List<T>", or None.""" |
| 135 match = re.match(r'List<(\w*)>$', type_name) |
| 136 if match: |
| 137 return match.group(1) |
| 138 return None |
| 139 |
| 140 def MaybeListElementType(interface): |
| 141 """Returns the List element type T, or None in interface does not implement |
| 142 List<T>. |
| 143 """ |
| 144 for parent in interface.parents: |
| 145 element_type = MaybeListElementTypeName(parent.type.id) |
| 146 if element_type: |
| 147 return element_type |
| 148 return None |
| 149 |
| 150 def MaybeTypedArrayElementType(interface): |
| 151 """Returns the typed array element type, or None in interface is not a |
| 152 TypedArray. |
| 153 """ |
| 154 # Typed arrays implement ArrayBufferView and List<T>. |
| 155 for parent in interface.parents: |
| 156 if parent.type.id == 'ArrayBufferView': |
| 157 return MaybeListElementType(interface) |
| 158 if parent.type.id == 'Uint8Array': |
| 159 return 'int' |
| 160 return None |
| 161 |
| 162 def MakeNativeSpec(javascript_binding_name): |
| 163 if javascript_binding_name in _frog_dom_custom_native_specs: |
| 164 return _frog_dom_custom_native_specs[javascript_binding_name] |
| 165 else: |
| 166 # Make the class 'hidden' so it is dynamically patched at runtime. This |
| 167 # is useful not only for browser compat, but to allow code that links |
| 168 # against dart:dom to load in a worker isolate. |
| 169 return '*' + javascript_binding_name |
| 170 |
| 171 |
| 172 def MatchSourceFilter(filter, thing): |
| 173 if not filter: |
| 174 return True |
| 175 else: |
| 176 return any(token in thing.annotations for token in filter) |
| 177 |
| 178 def AnalyzeOperation(interface, operations): |
| 179 """Makes operation calling convention decision for a set of overloads. |
| 180 |
| 181 Returns: An OperationInfo object. |
| 182 """ |
| 183 |
| 184 # Zip together arguments from each overload by position, then convert |
| 185 # to a dart argument. |
| 186 |
| 187 # Given a list of overloaded arguments, choose a suitable name. |
| 188 def OverloadedName(args): |
| 189 return '_OR_'.join(sorted(set(arg.id for arg in args))) |
| 190 |
| 191 # Given a list of overloaded arguments, choose a suitable type. |
| 192 def OverloadedType(args): |
| 193 typeIds = sorted(set(arg.type.id for arg in args)) |
| 194 if len(typeIds) == 1: |
| 195 return typeIds[0] |
| 196 else: |
| 197 return TypeName(typeIds, interface) |
| 198 |
| 199 # Given a list of overloaded arguments, render a dart argument. |
| 200 def DartArg(args): |
| 201 filtered = filter(None, args) |
| 202 optional = any(not arg or arg.is_optional for arg in args) |
| 203 type = OverloadedType(filtered) |
| 204 name = OverloadedName(filtered) |
| 205 if optional: |
| 206 return (name, type, 'null') |
| 207 else: |
| 208 return (name, type, None) |
| 209 |
| 210 args = map(lambda *args: DartArg(args), |
| 211 *(op.arguments for op in operations)) |
| 212 |
| 213 info = OperationInfo() |
| 214 info.overloads = operations |
| 215 info.declared_name = operations[0].id |
| 216 info.name = operations[0].ext_attrs.get('DartName', info.declared_name) |
| 217 info.js_name = info.declared_name |
| 218 info.type_name = operations[0].type.id # TODO: widen. |
| 219 info.arg_infos = args |
| 220 return info |
| 221 |
| 222 def RecognizeCallback(interface): |
| 223 """Returns the info for the callback method if the interface smells like a |
| 224 callback. |
| 225 """ |
| 226 if 'Callback' not in interface.ext_attrs: return None |
| 227 handlers = [op for op in interface.operations if op.id == 'handleEvent'] |
| 228 if not handlers: return None |
| 229 if not (handlers == interface.operations): return None |
| 230 return AnalyzeOperation(interface, handlers) |
| 231 |
| 232 def IsDartListType(type): |
| 233 return type == 'List' or type.startswith('List<') |
| 234 |
| 235 def IsDartCollectionType(type): |
| 236 return IsDartListType(type) |
| 237 |
| 238 def FindMatchingAttribute(interface, attr1): |
| 239 matches = [attr2 for attr2 in interface.attributes |
| 240 if attr1.id == attr2.id |
| 241 and attr1.is_fc_getter == attr2.is_fc_getter |
| 242 and attr1.is_fc_setter == attr2.is_fc_setter] |
| 243 if matches: |
| 244 assert len(matches) == 1 |
| 245 return matches[0] |
| 246 return None |
| 247 |
| 248 class OperationInfo(object): |
| 249 """Holder for various derived information from a set of overloaded operations. |
| 250 |
| 251 Attributes: |
| 252 overloads: A list of IDL operation overloads with the same name. |
| 253 name: A string, the simple name of the operation. |
| 254 type_name: A string, the name of the return type of the operation. |
| 255 arg_infos: A list of (name, type, default_value) tuples. |
| 256 default_value is None for mandatory arguments. |
| 257 """ |
| 258 |
| 259 def ParametersInterfaceDeclaration(self): |
| 260 """Returns a formatted string declaring the parameters for the interface.""" |
| 261 return self._FormatArgs(self.arg_infos, True) |
| 262 |
| 263 def ParametersImplementationDeclaration(self, rename_type=None): |
| 264 """Returns a formatted string declaring the parameters for the |
| 265 implementation. |
| 266 |
| 267 Args: |
| 268 rename_type: A function that allows the types to be renamed. |
| 269 """ |
| 270 args = self.arg_infos |
| 271 if rename_type: |
| 272 args = [(name, rename_type(type), default) |
| 273 for (name, type, default) in args] |
| 274 return self._FormatArgs(args, False) |
| 275 |
| 276 def ParametersAsArgumentList(self): |
| 277 """Returns a formatted string declaring the parameters names as an argument |
| 278 list. |
| 279 """ |
| 280 return ', '.join(map(lambda arg_info: arg_info[0], self.arg_infos)) |
| 281 |
| 282 def _FormatArgs(self, args, is_interface): |
| 283 def FormatArg(arg_info): |
| 284 """Returns an argument declaration fragment for an argument info tuple.""" |
| 285 (name, type, default) = arg_info |
| 286 if default: |
| 287 return '%s %s = %s' % (type, name, default) |
| 288 else: |
| 289 return '%s %s' % (type, name) |
| 290 |
| 291 required = [] |
| 292 optional = [] |
| 293 for (name, type, default) in args: |
| 294 if default: |
| 295 if is_interface: |
| 296 optional.append((name, type, None)) # Default values illegal. |
| 297 else: |
| 298 optional.append((name, type, default)) |
| 299 else: |
| 300 if optional: |
| 301 raise Exception('Optional arguments cannot precede required ones: ' |
| 302 + str(args)) |
| 303 required.append((name, type, None)) |
| 304 argtexts = map(FormatArg, required) |
| 305 if optional: |
| 306 argtexts.append('[' + ', '.join(map(FormatArg, optional)) + ']') |
| 307 return ', '.join(argtexts) |
| 308 |
| 309 |
| 310 def AttributeOutputOrder(a, b): |
| 311 """Canonical output ordering for attributes.""" |
| 312 # Getters before setters: |
| 313 if a.id < b.id: return -1 |
| 314 if a.id > b.id: return 1 |
| 315 if a.is_fc_setter < b.is_fc_setter: return -1 |
| 316 if a.is_fc_setter > b.is_fc_setter: return 1 |
| 317 return 0 |
| 318 |
| 319 def ConstantOutputOrder(a, b): |
| 320 """Canonical output ordering for constants.""" |
| 321 if a.id < b.id: return -1 |
| 322 if a.id > b.id: return 1 |
| 323 return 0 |
| 324 |
| 325 |
| 326 def _FormatNameList(names): |
| 327 """Returns JavaScript array literal expression with one name per line.""" |
| 328 #names = sorted(names) |
| 329 if len(names) <= 1: |
| 330 expression_string = str(names) # e.g. ['length'] |
| 331 else: |
| 332 expression_string = ',\n '.join(str(names).split(',')) |
| 333 expression_string = expression_string.replace('[', '[\n ') |
| 334 return expression_string |
| 335 |
| 336 |
| 337 def IndentText(text, indent): |
| 338 """Format lines of text with indent.""" |
| 339 def FormatLine(line): |
| 340 if line.strip(): |
| 341 return '%s%s\n' % (indent, line) |
| 342 else: |
| 343 return '\n' |
| 344 return ''.join(FormatLine(line) for line in text.split('\n')) |
| 345 |
| 346 # Given a sorted sequence of type identifiers, return an appropriate type |
| 347 # name |
| 348 def TypeName(typeIds, interface): |
| 349 # Dynamically type this field for now. |
| 350 return 'var' |
| 351 |
OLD | NEW |