|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 # You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 from WebIDL import IDLInterface, IDLExternalInterface |
|
6 import os |
|
7 |
|
8 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" |
|
9 |
|
10 class Configuration: |
|
11 """ |
|
12 Represents global configuration state based on IDL parse data and |
|
13 the configuration file. |
|
14 """ |
|
15 def __init__(self, filename, parseData): |
|
16 |
|
17 # Read the configuration file. |
|
18 glbl = {} |
|
19 execfile(filename, glbl) |
|
20 config = glbl['DOMInterfaces'] |
|
21 |
|
22 # Build descriptors for all the interfaces we have in the parse data. |
|
23 # This allows callers to specify a subset of interfaces by filtering |
|
24 # |parseData|. |
|
25 self.descriptors = [] |
|
26 self.interfaces = {} |
|
27 self.maxProtoChainLength = 0; |
|
28 for thing in parseData: |
|
29 # Some toplevel things are sadly types, and those have an |
|
30 # isInterface that doesn't mean the same thing as IDLObject's |
|
31 # isInterface()... |
|
32 if (not isinstance(thing, IDLInterface) and |
|
33 not isinstance(thing, IDLExternalInterface)): |
|
34 continue |
|
35 iface = thing |
|
36 self.interfaces[iface.identifier.name] = iface |
|
37 if iface.identifier.name not in config: |
|
38 # Completely skip consequential interfaces with no descriptor |
|
39 # if they have no interface object because chances are we |
|
40 # don't need to do anything interesting with them. |
|
41 if iface.isConsequential() and not iface.hasInterfaceObject(): |
|
42 continue |
|
43 entry = {} |
|
44 else: |
|
45 entry = config[iface.identifier.name] |
|
46 if not isinstance(entry, list): |
|
47 assert isinstance(entry, dict) |
|
48 entry = [entry] |
|
49 elif len(entry) == 1: |
|
50 if entry[0].get("workers", False): |
|
51 # List with only a workers descriptor means we should |
|
52 # infer a mainthread descriptor. If you want only |
|
53 # workers bindings, don't use a list here. |
|
54 entry.append({}) |
|
55 else: |
|
56 raise TypeError("Don't use a single-element list for " |
|
57 "non-worker-only interface " + iface.identifier.name + |
|
58 " in Bindings.conf") |
|
59 elif len(entry) == 2: |
|
60 if entry[0].get("workers", False) == entry[1].get("workers", False): |
|
61 raise TypeError("The two entries for interface " + iface.identifier.name + |
|
62 " in Bindings.conf should not have the same value for 'workers'") |
|
63 else: |
|
64 raise TypeError("Interface " + iface.identifier.name + |
|
65 " should have no more than two entries in Bindings.conf") |
|
66 self.descriptors.extend([Descriptor(self, iface, x) for x in entry]) |
|
67 |
|
68 # Keep the descriptor list sorted for determinism. |
|
69 self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) |
|
70 |
|
71 self.descriptorsByName = {} |
|
72 for d in self.descriptors: |
|
73 self.descriptorsByName.setdefault(d.interface.identifier.name, |
|
74 []).append(d) |
|
75 |
|
76 self.descriptorsByFile = {} |
|
77 for d in self.descriptors: |
|
78 self.descriptorsByFile.setdefault(d.interface.filename(), |
|
79 []).append(d) |
|
80 |
|
81 self.enums = [e for e in parseData if e.isEnum()] |
|
82 |
|
83 # Figure out what our main-thread and worker dictionaries and callbacks |
|
84 # are. |
|
85 mainTypes = set() |
|
86 for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] + |
|
87 self.getDescriptors(workers=False, isExternal=False, skipGen=False)): |
|
88 mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) |
|
89 (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes) |
|
90 |
|
91 workerTypes = set(); |
|
92 for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] + |
|
93 self.getDescriptors(workers=True, isExternal=False, skipGen=False)): |
|
94 workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) |
|
95 (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes) |
|
96 |
|
97 self.dictionaries = [d for d in parseData if d.isDictionary()] |
|
98 self.callbacks = [c for c in parseData if |
|
99 c.isCallback() and not c.isInterface()] |
|
100 |
|
101 def flagWorkerOrMainThread(items, main, worker): |
|
102 for item in items: |
|
103 if item in main: |
|
104 item.setUserData("mainThread", True) |
|
105 if item in worker: |
|
106 item.setUserData("workers", True) |
|
107 flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks) |
|
108 |
|
109 def getInterface(self, ifname): |
|
110 return self.interfaces[ifname] |
|
111 def getDescriptors(self, **filters): |
|
112 """Gets the descriptors that match the given filters.""" |
|
113 curr = self.descriptors |
|
114 # Collect up our filters, because we may have a webIDLFile filter that |
|
115 # we always want to apply first. |
|
116 tofilter = [] |
|
117 for key, val in filters.iteritems(): |
|
118 if key == 'webIDLFile': |
|
119 # Special-case this part to make it fast, since most of our |
|
120 # getDescriptors calls are conditioned on a webIDLFile. We may |
|
121 # not have this key, in which case we have no descriptors |
|
122 # either. |
|
123 curr = self.descriptorsByFile.get(val, []) |
|
124 continue |
|
125 elif key == 'hasInterfaceObject': |
|
126 getter = lambda x: (not x.interface.isExternal() and |
|
127 x.interface.hasInterfaceObject()) |
|
128 elif key == 'hasInterfacePrototypeObject': |
|
129 getter = lambda x: (not x.interface.isExternal() and |
|
130 x.interface.hasInterfacePrototypeObject()) |
|
131 elif key == 'hasInterfaceOrInterfacePrototypeObject': |
|
132 getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject() |
|
133 elif key == 'isCallback': |
|
134 getter = lambda x: x.interface.isCallback() |
|
135 elif key == 'isExternal': |
|
136 getter = lambda x: x.interface.isExternal() |
|
137 elif key == 'isJSImplemented': |
|
138 getter = lambda x: x.interface.isJSImplemented() |
|
139 elif key == 'isNavigatorProperty': |
|
140 getter = lambda x: x.interface.getNavigatorProperty() != None |
|
141 else: |
|
142 # Have to watch out: just closing over "key" is not enough, |
|
143 # since we're about to mutate its value |
|
144 getter = (lambda attrName: lambda x: getattr(x, attrName))(key) |
|
145 tofilter.append((getter, val)) |
|
146 for f in tofilter: |
|
147 curr = filter(lambda x: f[0](x) == f[1], curr) |
|
148 return curr |
|
149 def getEnums(self, webIDLFile): |
|
150 return filter(lambda e: e.filename() == webIDLFile, self.enums) |
|
151 |
|
152 @staticmethod |
|
153 def _filterForFileAndWorkers(items, filters): |
|
154 """Gets the items that match the given filters.""" |
|
155 for key, val in filters.iteritems(): |
|
156 if key == 'webIDLFile': |
|
157 items = filter(lambda x: x.filename() == val, items) |
|
158 elif key == 'workers': |
|
159 if val: |
|
160 items = filter(lambda x: x.getUserData("workers", False), items) |
|
161 else: |
|
162 items = filter(lambda x: x.getUserData("mainThread", False), items) |
|
163 else: |
|
164 assert(0) # Unknown key |
|
165 return items |
|
166 def getDictionaries(self, **filters): |
|
167 return self._filterForFileAndWorkers(self.dictionaries, filters) |
|
168 def getCallbacks(self, **filters): |
|
169 return self._filterForFileAndWorkers(self.callbacks, filters) |
|
170 |
|
171 def getDescriptor(self, interfaceName, workers): |
|
172 """ |
|
173 Gets the appropriate descriptor for the given interface name |
|
174 and the given workers boolean. |
|
175 """ |
|
176 for d in self.descriptorsByName[interfaceName]: |
|
177 if d.workers == workers: |
|
178 return d |
|
179 |
|
180 if workers: |
|
181 for d in self.descriptorsByName[interfaceName]: |
|
182 return d |
|
183 |
|
184 raise NoSuchDescriptorError("For " + interfaceName + " found no matches"); |
|
185 def getDescriptorProvider(self, workers): |
|
186 """ |
|
187 Gets a descriptor provider that can provide descriptors as needed, |
|
188 for the given workers boolean |
|
189 """ |
|
190 return DescriptorProvider(self, workers) |
|
191 |
|
192 class NoSuchDescriptorError(TypeError): |
|
193 def __init__(self, str): |
|
194 TypeError.__init__(self, str) |
|
195 |
|
196 class DescriptorProvider: |
|
197 """ |
|
198 A way of getting descriptors for interface names |
|
199 """ |
|
200 def __init__(self, config, workers): |
|
201 self.config = config |
|
202 self.workers = workers |
|
203 |
|
204 def getDescriptor(self, interfaceName): |
|
205 """ |
|
206 Gets the appropriate descriptor for the given interface name given the |
|
207 context of the current descriptor. This selects the appropriate |
|
208 implementation for cases like workers. |
|
209 """ |
|
210 return self.config.getDescriptor(interfaceName, self.workers) |
|
211 |
|
212 class Descriptor(DescriptorProvider): |
|
213 """ |
|
214 Represents a single descriptor for an interface. See Bindings.conf. |
|
215 """ |
|
216 def __init__(self, config, interface, desc): |
|
217 DescriptorProvider.__init__(self, config, desc.get('workers', False)) |
|
218 self.interface = interface |
|
219 |
|
220 # Read the desc, and fill in the relevant defaults. |
|
221 ifaceName = self.interface.identifier.name |
|
222 if self.interface.isExternal(): |
|
223 if self.workers: |
|
224 nativeTypeDefault = "JSObject" |
|
225 else: |
|
226 nativeTypeDefault = "nsIDOM" + ifaceName |
|
227 elif self.interface.isCallback(): |
|
228 nativeTypeDefault = "mozilla::dom::" + ifaceName |
|
229 else: |
|
230 if self.workers: |
|
231 nativeTypeDefault = "mozilla::dom::workers::" + ifaceName |
|
232 else: |
|
233 nativeTypeDefault = "mozilla::dom::" + ifaceName |
|
234 |
|
235 self.nativeType = desc.get('nativeType', nativeTypeDefault) |
|
236 # Now create a version of nativeType that doesn't have extra |
|
237 # mozilla::dom:: at the beginning. |
|
238 prettyNativeType = self.nativeType.split("::") |
|
239 if prettyNativeType[0] == "mozilla": |
|
240 prettyNativeType.pop(0) |
|
241 if prettyNativeType[0] == "dom": |
|
242 prettyNativeType.pop(0) |
|
243 self.prettyNativeType = "::".join(prettyNativeType) |
|
244 |
|
245 self.jsImplParent = desc.get('jsImplParent', self.nativeType) |
|
246 |
|
247 # Do something sane for JSObject |
|
248 if self.nativeType == "JSObject": |
|
249 headerDefault = "js/TypeDecls.h" |
|
250 elif self.interface.isCallback() or self.interface.isJSImplemented(): |
|
251 # A copy of CGHeaders.getDeclarationFilename; we can't |
|
252 # import it here, sadly. |
|
253 # Use our local version of the header, not the exported one, so that |
|
254 # test bindings, which don't export, will work correctly. |
|
255 basename = os.path.basename(self.interface.filename()) |
|
256 headerDefault = basename.replace('.webidl', 'Binding.h') |
|
257 else: |
|
258 if self.workers: |
|
259 headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName |
|
260 elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"): |
|
261 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] |
|
262 else: |
|
263 headerDefault = self.nativeType |
|
264 headerDefault = headerDefault.replace("::", "/") + ".h" |
|
265 self.headerFile = desc.get('headerFile', headerDefault) |
|
266 self.headerIsDefault = self.headerFile == headerDefault |
|
267 if self.jsImplParent == self.nativeType: |
|
268 self.jsImplParentHeader = self.headerFile |
|
269 else: |
|
270 self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" |
|
271 |
|
272 self.skipGen = desc.get('skipGen', False) |
|
273 |
|
274 self.notflattened = desc.get('notflattened', False) |
|
275 self.register = desc.get('register', True) |
|
276 |
|
277 self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False) |
|
278 |
|
279 # If we're concrete, we need to crawl our ancestor interfaces and mark |
|
280 # them as having a concrete descendant. |
|
281 self.concrete = (not self.interface.isExternal() and |
|
282 not self.interface.isCallback() and |
|
283 desc.get('concrete', True)) |
|
284 operations = { |
|
285 'IndexedGetter': None, |
|
286 'IndexedSetter': None, |
|
287 'IndexedCreator': None, |
|
288 'IndexedDeleter': None, |
|
289 'NamedGetter': None, |
|
290 'NamedSetter': None, |
|
291 'NamedCreator': None, |
|
292 'NamedDeleter': None, |
|
293 'Stringifier': None, |
|
294 'LegacyCaller': None, |
|
295 'Jsonifier': None |
|
296 } |
|
297 if self.concrete: |
|
298 self.proxy = False |
|
299 iface = self.interface |
|
300 def addOperation(operation, m): |
|
301 if not operations[operation]: |
|
302 operations[operation] = m |
|
303 # Since stringifiers go on the prototype, we only need to worry |
|
304 # about our own stringifier, not those of our ancestor interfaces. |
|
305 for m in iface.members: |
|
306 if m.isMethod() and m.isStringifier(): |
|
307 addOperation('Stringifier', m) |
|
308 if m.isMethod() and m.isJsonifier(): |
|
309 addOperation('Jsonifier', m) |
|
310 # Don't worry about inheriting legacycallers either: in |
|
311 # practice these are on most-derived prototypes. |
|
312 if m.isMethod() and m.isLegacycaller(): |
|
313 if not m.isIdentifierLess(): |
|
314 raise TypeError("We don't support legacycaller with " |
|
315 "identifier.\n%s" % m.location); |
|
316 if len(m.signatures()) != 1: |
|
317 raise TypeError("We don't support overloaded " |
|
318 "legacycaller.\n%s" % m.location) |
|
319 addOperation('LegacyCaller', m) |
|
320 while iface: |
|
321 for m in iface.members: |
|
322 if not m.isMethod(): |
|
323 continue |
|
324 |
|
325 def addIndexedOrNamedOperation(operation, m): |
|
326 self.proxy = True |
|
327 if m.isIndexed(): |
|
328 operation = 'Indexed' + operation |
|
329 else: |
|
330 assert m.isNamed() |
|
331 operation = 'Named' + operation |
|
332 addOperation(operation, m) |
|
333 |
|
334 if m.isGetter(): |
|
335 addIndexedOrNamedOperation('Getter', m) |
|
336 if m.isSetter(): |
|
337 addIndexedOrNamedOperation('Setter', m) |
|
338 if m.isCreator(): |
|
339 addIndexedOrNamedOperation('Creator', m) |
|
340 if m.isDeleter(): |
|
341 addIndexedOrNamedOperation('Deleter', m) |
|
342 if m.isLegacycaller() and iface != self.interface: |
|
343 raise TypeError("We don't support legacycaller on " |
|
344 "non-leaf interface %s.\n%s" % |
|
345 (iface, iface.location)) |
|
346 |
|
347 iface.setUserData('hasConcreteDescendant', True) |
|
348 iface = iface.parent |
|
349 |
|
350 if self.proxy: |
|
351 if (not operations['IndexedGetter'] and |
|
352 (operations['IndexedSetter'] or |
|
353 operations['IndexedDeleter'] or |
|
354 operations['IndexedCreator'])): |
|
355 raise SyntaxError("%s supports indexed properties but does " |
|
356 "not have an indexed getter.\n%s" % |
|
357 (self.interface, self.interface.location)) |
|
358 if (not operations['NamedGetter'] and |
|
359 (operations['NamedSetter'] or |
|
360 operations['NamedDeleter'] or |
|
361 operations['NamedCreator'])): |
|
362 raise SyntaxError("%s supports named properties but does " |
|
363 "not have a named getter.\n%s" % |
|
364 (self.interface, self.interface.location)) |
|
365 iface = self.interface |
|
366 while iface: |
|
367 iface.setUserData('hasProxyDescendant', True) |
|
368 iface = iface.parent |
|
369 self.operations = operations |
|
370 |
|
371 self.nativeOwnership = desc.get('nativeOwnership', 'refcounted') |
|
372 if not self.nativeOwnership in ('owned', 'refcounted'): |
|
373 raise TypeError("Descriptor for %s has unrecognized value (%s) " |
|
374 "for nativeOwnership" % |
|
375 (self.interface.identifier.name, self.nativeOwnership)) |
|
376 if desc.get('wantsQI', None) != None: |
|
377 self._wantsQI = desc.get('wantsQI', None) |
|
378 self.wrapperCache = (not self.interface.isCallback() and |
|
379 (self.nativeOwnership != 'owned' and |
|
380 desc.get('wrapperCache', True))) |
|
381 |
|
382 def make_name(name): |
|
383 return name + "_workers" if self.workers else name |
|
384 self.name = make_name(interface.identifier.name) |
|
385 |
|
386 # self.extendedAttributes is a dict of dicts, keyed on |
|
387 # all/getterOnly/setterOnly and then on member name. Values are an |
|
388 # array of extended attributes. |
|
389 self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} } |
|
390 |
|
391 def addExtendedAttribute(attribute, config): |
|
392 def add(key, members, attribute): |
|
393 for member in members: |
|
394 self.extendedAttributes[key].setdefault(member, []).append(attribute) |
|
395 |
|
396 if isinstance(config, dict): |
|
397 for key in ['all', 'getterOnly', 'setterOnly']: |
|
398 add(key, config.get(key, []), attribute) |
|
399 elif isinstance(config, list): |
|
400 add('all', config, attribute) |
|
401 else: |
|
402 assert isinstance(config, str) |
|
403 if config == '*': |
|
404 iface = self.interface |
|
405 while iface: |
|
406 add('all', map(lambda m: m.name, iface.members), attribute) |
|
407 iface = iface.parent |
|
408 else: |
|
409 add('all', [config], attribute) |
|
410 |
|
411 if self.interface.isJSImplemented(): |
|
412 addExtendedAttribute('implicitJSContext', ['constructor']) |
|
413 else: |
|
414 for attribute in ['implicitJSContext', 'resultNotAddRefed']: |
|
415 addExtendedAttribute(attribute, desc.get(attribute, {})) |
|
416 |
|
417 self.binaryNames = desc.get('binaryNames', {}) |
|
418 if '__legacycaller' not in self.binaryNames: |
|
419 self.binaryNames["__legacycaller"] = "LegacyCall" |
|
420 if '__stringifier' not in self.binaryNames: |
|
421 self.binaryNames["__stringifier"] = "Stringify" |
|
422 |
|
423 # Build the prototype chain. |
|
424 self.prototypeChain = [] |
|
425 parent = interface |
|
426 while parent: |
|
427 self.prototypeChain.insert(0, parent.identifier.name) |
|
428 parent = parent.parent |
|
429 config.maxProtoChainLength = max(config.maxProtoChainLength, |
|
430 len(self.prototypeChain)) |
|
431 |
|
432 def hasInterfaceOrInterfacePrototypeObject(self): |
|
433 |
|
434 # Forward-declared interfaces don't need either interface object or |
|
435 # interface prototype object as they're going to use QI (on main thread) |
|
436 # or be passed as a JSObject (on worker threads). |
|
437 if self.interface.isExternal(): |
|
438 return False |
|
439 |
|
440 return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() |
|
441 |
|
442 def getExtendedAttributes(self, member, getter=False, setter=False): |
|
443 def ensureValidThrowsExtendedAttribute(attr): |
|
444 assert(attr is None or attr is True or len(attr) == 1) |
|
445 if (attr is not None and attr is not True and |
|
446 'Workers' not in attr and 'MainThread' not in attr): |
|
447 raise TypeError("Unknown value for 'Throws': " + attr[0]) |
|
448 |
|
449 def maybeAppendInfallibleToAttrs(attrs, throws): |
|
450 ensureValidThrowsExtendedAttribute(throws) |
|
451 if (throws is None or |
|
452 (throws is not True and |
|
453 ('Workers' not in throws or not self.workers) and |
|
454 ('MainThread' not in throws or self.workers))): |
|
455 attrs.append("infallible") |
|
456 |
|
457 name = member.identifier.name |
|
458 throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws") |
|
459 if member.isMethod(): |
|
460 attrs = self.extendedAttributes['all'].get(name, []) |
|
461 maybeAppendInfallibleToAttrs(attrs, throws) |
|
462 return attrs |
|
463 |
|
464 assert member.isAttr() |
|
465 assert bool(getter) != bool(setter) |
|
466 key = 'getterOnly' if getter else 'setterOnly' |
|
467 attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, []) |
|
468 if throws is None: |
|
469 throwsAttr = "GetterThrows" if getter else "SetterThrows" |
|
470 throws = member.getExtendedAttribute(throwsAttr) |
|
471 maybeAppendInfallibleToAttrs(attrs, throws) |
|
472 return attrs |
|
473 |
|
474 def supportsIndexedProperties(self): |
|
475 return self.operations['IndexedGetter'] is not None |
|
476 |
|
477 def supportsNamedProperties(self): |
|
478 return self.operations['NamedGetter'] is not None |
|
479 |
|
480 def needsConstructHookHolder(self): |
|
481 assert self.interface.hasInterfaceObject() |
|
482 return False |
|
483 |
|
484 def needsHeaderInclude(self): |
|
485 """ |
|
486 An interface doesn't need a header file if it is not concrete, |
|
487 not pref-controlled, has no prototype object, and has no |
|
488 static methods or attributes. |
|
489 """ |
|
490 return (self.interface.isExternal() or self.concrete or |
|
491 self.interface.hasInterfacePrototypeObject() or |
|
492 any((m.isAttr() or m.isMethod()) and m.isStatic() for m |
|
493 in self.interface.members)) |
|
494 |
|
495 def isExposedConditionally(self): |
|
496 return (self.interface.getExtendedAttribute("Pref") or |
|
497 self.interface.getExtendedAttribute("ChromeOnly") or |
|
498 self.interface.getExtendedAttribute("Func") or |
|
499 self.interface.getExtendedAttribute("AvailableIn")) |
|
500 |
|
501 def needsXrayResolveHooks(self): |
|
502 """ |
|
503 Generally, any interface with NeedNewResolve needs Xray |
|
504 resolveOwnProperty and enumerateOwnProperties hooks. But for |
|
505 the special case of plugin-loading elements, we do NOT want |
|
506 those, because we don't want to instantiate plug-ins simply |
|
507 due to chrome touching them and that's all those hooks do on |
|
508 those elements. So we special-case those here. |
|
509 """ |
|
510 return (self.interface.getExtendedAttribute("NeedNewResolve") and |
|
511 self.interface.identifier.name not in ["HTMLObjectElement", |
|
512 "HTMLEmbedElement", |
|
513 "HTMLAppletElement"]) |
|
514 |
|
515 def needsSpecialGenericOps(self): |
|
516 """ |
|
517 Returns true if this descriptor requires generic ops other than |
|
518 GenericBindingMethod/GenericBindingGetter/GenericBindingSetter. |
|
519 |
|
520 In practice we need to do this if our this value might be an XPConnect |
|
521 object or if we need to coerce null/undefined to the global. |
|
522 """ |
|
523 return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() |
|
524 |
|
525 # Some utility methods |
|
526 def getTypesFromDescriptor(descriptor): |
|
527 """ |
|
528 Get all argument and return types for all members of the descriptor |
|
529 """ |
|
530 members = [m for m in descriptor.interface.members] |
|
531 if descriptor.interface.ctor(): |
|
532 members.append(descriptor.interface.ctor()) |
|
533 members.extend(descriptor.interface.namedConstructors) |
|
534 signatures = [s for m in members if m.isMethod() for s in m.signatures()] |
|
535 types = [] |
|
536 for s in signatures: |
|
537 assert len(s) == 2 |
|
538 (returnType, arguments) = s |
|
539 types.append(returnType) |
|
540 types.extend(a.type for a in arguments) |
|
541 |
|
542 types.extend(a.type for a in members if a.isAttr()) |
|
543 return types |
|
544 |
|
545 def getFlatTypes(types): |
|
546 retval = set() |
|
547 for type in types: |
|
548 type = type.unroll() |
|
549 if type.isUnion(): |
|
550 retval |= set(type.flatMemberTypes) |
|
551 else: |
|
552 retval.add(type) |
|
553 return retval |
|
554 |
|
555 def getTypesFromDictionary(dictionary): |
|
556 """ |
|
557 Get all member types for this dictionary |
|
558 """ |
|
559 types = [] |
|
560 curDict = dictionary |
|
561 while curDict: |
|
562 types.extend([m.type for m in curDict.members]) |
|
563 curDict = curDict.parent |
|
564 return types |
|
565 |
|
566 def getTypesFromCallback(callback): |
|
567 """ |
|
568 Get the types this callback depends on: its return type and the |
|
569 types of its arguments. |
|
570 """ |
|
571 sig = callback.signatures()[0] |
|
572 types = [sig[0]] # Return type |
|
573 types.extend(arg.type for arg in sig[1]) # Arguments |
|
574 return types |
|
575 |
|
576 def findCallbacksAndDictionaries(inputTypes): |
|
577 """ |
|
578 Ensure that all callbacks and dictionaries reachable from types end up in |
|
579 the returned callbacks and dictionaries sets. |
|
580 |
|
581 Note that we assume that our initial invocation already includes all types |
|
582 reachable via descriptors in "types", so we only have to deal with things |
|
583 that are themeselves reachable via callbacks and dictionaries. |
|
584 """ |
|
585 def doFindCallbacksAndDictionaries(types, callbacks, dictionaries): |
|
586 unhandledTypes = set() |
|
587 for type in types: |
|
588 if type.isCallback() and type not in callbacks: |
|
589 unhandledTypes |= getFlatTypes(getTypesFromCallback(type)) |
|
590 callbacks.add(type) |
|
591 elif type.isDictionary() and type.inner not in dictionaries: |
|
592 d = type.inner |
|
593 unhandledTypes |= getFlatTypes(getTypesFromDictionary(d)) |
|
594 while d: |
|
595 dictionaries.add(d) |
|
596 d = d.parent |
|
597 if len(unhandledTypes) != 0: |
|
598 doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries) |
|
599 |
|
600 retCallbacks = set() |
|
601 retDictionaries = set() |
|
602 doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries) |
|
603 return (retCallbacks, retDictionaries) |