Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / scripts / idl_definitions.py
1 # Copyright (C) 2013 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 #     * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #     * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 #     * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 """Blink IDL Intermediate Representation (IR) classes.
30
31 Classes are primarily constructors, which build an IdlDefinitions object
32 (and various contained objects) from an AST (produced by blink_idl_parser).
33
34 This is in two steps:
35 * Constructors walk the AST, creating objects.
36 * Typedef resolution.
37
38 Typedefs are all resolved here, and not stored in IR.
39
40 Typedef resolution uses some auxiliary classes and OOP techniques to make this
41 a generic call, via the resolve_typedefs() method.
42
43 Class hierarchy (mostly containment, '<' for inheritance):
44
45 IdlDefinitions
46     IdlCallbackFunction < TypedObject
47     IdlEnum :: FIXME: remove, just use a dict for enums
48     IdlInterface
49         IdlAttribute < TypedObject
50         IdlConstant < TypedObject
51         IdlLiteral
52         IdlOperation < TypedObject
53             IdlArgument < TypedObject
54         IdlStringifier
55     IdlException < IdlInterface
56         (same contents as IdlInterface)
57
58 TypedObject :: mixin for typedef resolution
59
60 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
61 """
62
63 import abc
64
65 from idl_types import IdlType, IdlUnionType, IdlArrayType, IdlSequenceType, IdlNullableType
66
67 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
68 STANDARD_TYPEDEFS = {
69     # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp
70     'DOMTimeStamp': 'unsigned long long',
71 }
72
73
74 ################################################################################
75 # TypedObject (mixin for typedef resolution)
76 ################################################################################
77
78 class TypedObject(object):
79     """Object with a type, such as an Attribute or Operation (return value).
80
81     The type can be an actual type, or can be a typedef, which must be resolved
82     before passing data to the code generator.
83     """
84     __metaclass__ = abc.ABCMeta
85     idl_type = None
86
87     def resolve_typedefs(self, typedefs):
88         """Resolve typedefs to actual types in the object."""
89         # Constructors don't have their own return type, because it's the
90         # interface itself.
91         if not self.idl_type:
92             return
93         # Need to re-assign self.idl_type, not just mutate idl_type,
94         # since type(idl_type) may change.
95         self.idl_type = self.idl_type.resolve_typedefs(typedefs)
96
97
98 ################################################################################
99 # Definitions (main container class)
100 ################################################################################
101
102 class IdlDefinitions(object):
103     def __init__(self, idl_name, node):
104         """Args: node: AST root node, class == 'File'"""
105         self.callback_functions = {}
106         self.dictionaries = {}
107         self.enumerations = {}
108         self.interfaces = {}
109         self.idl_name = idl_name
110
111         node_class = node.GetClass()
112         if node_class != 'File':
113             raise ValueError('Unrecognized node class: %s' % node_class)
114
115         typedefs = dict((typedef_name, IdlType(type_name))
116                         for typedef_name, type_name in
117                         STANDARD_TYPEDEFS.iteritems())
118
119         children = node.GetChildren()
120         for child in children:
121             child_class = child.GetClass()
122             if child_class == 'Interface':
123                 interface = IdlInterface(idl_name, child)
124                 self.interfaces[interface.name] = interface
125             elif child_class == 'Exception':
126                 exception = IdlException(idl_name, child)
127                 # For simplicity, treat exceptions as interfaces
128                 self.interfaces[exception.name] = exception
129             elif child_class == 'Typedef':
130                 type_name = child.GetName()
131                 typedefs[type_name] = typedef_node_to_type(child)
132             elif child_class == 'Enum':
133                 enumeration = IdlEnum(idl_name, child)
134                 self.enumerations[enumeration.name] = enumeration
135             elif child_class == 'Callback':
136                 callback_function = IdlCallbackFunction(idl_name, child)
137                 self.callback_functions[callback_function.name] = callback_function
138             elif child_class == 'Implements':
139                 # Implements is handled at the interface merging step
140                 pass
141             elif child_class == 'Dictionary':
142                 dictionary = IdlDictionary(idl_name, child)
143                 self.dictionaries[dictionary.name] = dictionary
144             else:
145                 raise ValueError('Unrecognized node class: %s' % child_class)
146
147         # Typedefs are not stored in IR:
148         # Resolve typedefs with the actual types and then discard the Typedefs.
149         # http://www.w3.org/TR/WebIDL/#idl-typedefs
150         self.resolve_typedefs(typedefs)
151
152     def resolve_typedefs(self, typedefs):
153         for callback_function in self.callback_functions.itervalues():
154             callback_function.resolve_typedefs(typedefs)
155         for interface in self.interfaces.itervalues():
156             interface.resolve_typedefs(typedefs)
157
158     def update(self, other):
159         """Update with additional IdlDefinitions."""
160         for interface_name, new_interface in other.interfaces.iteritems():
161             if not new_interface.is_partial:
162                 # Add as new interface
163                 self.interfaces[interface_name] = new_interface
164                 continue
165
166             # Merge partial to existing interface
167             try:
168                 self.interfaces[interface_name].merge(new_interface)
169             except KeyError:
170                 raise Exception('Tried to merge partial interface for {0}, '
171                                 'but no existing interface by that name'
172                                 .format(interface_name))
173
174             # Merge callbacks and enumerations
175             self.enumerations.update(other.enumerations)
176             self.callback_functions.update(other.callback_functions)
177
178
179 ################################################################################
180 # Callback Functions
181 ################################################################################
182
183 class IdlCallbackFunction(TypedObject):
184     def __init__(self, idl_name, node):
185         children = node.GetChildren()
186         num_children = len(children)
187         if num_children != 2:
188             raise ValueError('Expected 2 children, got %s' % num_children)
189         type_node, arguments_node = children
190         arguments_node_class = arguments_node.GetClass()
191         if arguments_node_class != 'Arguments':
192             raise ValueError('Expected Arguments node, got %s' % arguments_node_class)
193
194         self.idl_name = idl_name
195         self.name = node.GetName()
196         self.idl_type = type_node_to_type(type_node)
197         self.arguments = arguments_node_to_arguments(idl_name, arguments_node)
198
199     def resolve_typedefs(self, typedefs):
200         TypedObject.resolve_typedefs(self, typedefs)
201         for argument in self.arguments:
202             argument.resolve_typedefs(typedefs)
203
204
205 ################################################################################
206 # Dictionary
207 ################################################################################
208
209 class IdlDictionary(object):
210     def __init__(self, idl_name, node):
211         self.extended_attributes = {}
212         self.is_partial = node.GetProperty('Partial') or False
213         self.idl_name = idl_name
214         self.name = node.GetName()
215         self.members = []
216         self.parent = None
217         for child in node.GetChildren():
218             child_class = child.GetClass()
219             if child_class == 'Inherit':
220                 self.parent = child.GetName()
221             elif child_class == 'Key':
222                 self.members.append(IdlDictionaryMember(idl_name, child))
223             elif child_class == 'ExtAttributes':
224                 self.extended_attributes = (
225                     ext_attributes_node_to_extended_attributes(idl_name, child))
226             else:
227                 raise ValueError('Unrecognized node class: %s' % child_class)
228
229
230 class IdlDictionaryMember(object):
231     def __init__(self, idl_name, node):
232         self.default_value = None
233         self.extended_attributes = {}
234         self.idl_type = None
235         self.idl_name = idl_name
236         self.name = node.GetName()
237         for child in node.GetChildren():
238             child_class = child.GetClass()
239             if child_class == 'Type':
240                 self.idl_type = type_node_to_type(child)
241             elif child_class == 'Default':
242                 self.default_value = default_node_to_idl_literal(child)
243             elif child_class == 'ExtAttributes':
244                 self.extended_attributes = (
245                     ext_attributes_node_to_extended_attributes(idl_name, child))
246             else:
247                 raise ValueError('Unrecognized node class: %s' % child_class)
248
249
250 ################################################################################
251 # Enumerations
252 ################################################################################
253
254 class IdlEnum(object):
255     # FIXME: remove, just treat enums as a dictionary
256     def __init__(self, idl_name, node):
257         self.idl_name = idl_name
258         self.name = node.GetName()
259         self.values = []
260         for child in node.GetChildren():
261             self.values.append(child.GetName())
262
263
264 ################################################################################
265 # Interfaces and Exceptions
266 ################################################################################
267
268 class IdlInterface(object):
269     def __init__(self, idl_name, node=None):
270         self.attributes = []
271         self.constants = []
272         self.constructors = []
273         self.custom_constructors = []
274         self.extended_attributes = {}
275         self.operations = []
276         self.parent = None
277         self.stringifier = None
278         if not node:  # Early exit for IdlException.__init__
279             return
280
281         self.is_callback = node.GetProperty('CALLBACK') or False
282         self.is_exception = False
283         # FIXME: uppercase 'Partial' => 'PARTIAL' in base IDL parser
284         self.is_partial = node.GetProperty('Partial') or False
285         self.idl_name = idl_name
286         self.name = node.GetName()
287
288         children = node.GetChildren()
289         for child in children:
290             child_class = child.GetClass()
291             if child_class == 'Attribute':
292                 self.attributes.append(IdlAttribute(idl_name, child))
293             elif child_class == 'Const':
294                 self.constants.append(IdlConstant(idl_name, child))
295             elif child_class == 'ExtAttributes':
296                 extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
297                 self.constructors, self.custom_constructors = (
298                     extended_attributes_to_constructors(idl_name, extended_attributes))
299                 clear_constructor_attributes(extended_attributes)
300                 self.extended_attributes = extended_attributes
301             elif child_class == 'Operation':
302                 self.operations.append(IdlOperation(idl_name, child))
303             elif child_class == 'Inherit':
304                 self.parent = child.GetName()
305             elif child_class == 'Stringifier':
306                 self.stringifier = IdlStringifier(idl_name, child)
307                 self.process_stringifier()
308             else:
309                 raise ValueError('Unrecognized node class: %s' % child_class)
310
311     def resolve_typedefs(self, typedefs):
312         for attribute in self.attributes:
313             attribute.resolve_typedefs(typedefs)
314         for constant in self.constants:
315             constant.resolve_typedefs(typedefs)
316         for constructor in self.constructors:
317             constructor.resolve_typedefs(typedefs)
318         for custom_constructor in self.custom_constructors:
319             custom_constructor.resolve_typedefs(typedefs)
320         for operation in self.operations:
321             operation.resolve_typedefs(typedefs)
322
323     def process_stringifier(self):
324         """Add the stringifier's attribute or named operation child, if it has
325         one, as a regular attribute/operation of this interface."""
326         if self.stringifier.attribute:
327             self.attributes.append(self.stringifier.attribute)
328         elif self.stringifier.operation:
329             self.operations.append(self.stringifier.operation)
330
331     def merge(self, other):
332         """Merge in another interface's members (e.g., partial interface)"""
333         self.attributes.extend(other.attributes)
334         self.constants.extend(other.constants)
335         self.operations.extend(other.operations)
336
337
338 class IdlException(IdlInterface):
339     # Properly exceptions and interfaces are distinct, and thus should inherit a
340     # common base class (say, "IdlExceptionOrInterface").
341     # However, there is only one exception (DOMException), and new exceptions
342     # are not expected. Thus it is easier to implement exceptions as a
343     # restricted subclass of interfaces.
344     # http://www.w3.org/TR/WebIDL/#idl-exceptions
345     def __init__(self, idl_name, node):
346         # Exceptions are similar to Interfaces, but simpler
347         IdlInterface.__init__(self, idl_name)
348         self.is_callback = False
349         self.is_exception = True
350         self.is_partial = False
351         self.idl_name = idl_name
352         self.name = node.GetName()
353
354         children = node.GetChildren()
355         for child in children:
356             child_class = child.GetClass()
357             if child_class == 'Attribute':
358                 attribute = IdlAttribute(idl_name, child)
359                 self.attributes.append(attribute)
360             elif child_class == 'Const':
361                 self.constants.append(IdlConstant(idl_name, child))
362             elif child_class == 'ExtAttributes':
363                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
364             elif child_class == 'ExceptionOperation':
365                 self.operations.append(IdlOperation.from_exception_operation_node(idl_name, child))
366             else:
367                 raise ValueError('Unrecognized node class: %s' % child_class)
368
369
370 ################################################################################
371 # Attributes
372 ################################################################################
373
374 class IdlAttribute(TypedObject):
375     def __init__(self, idl_name, node):
376         self.is_read_only = node.GetProperty('READONLY') or False
377         self.is_static = node.GetProperty('STATIC') or False
378         self.idl_name = idl_name
379         self.name = node.GetName()
380         # Defaults, overridden below
381         self.idl_type = None
382         self.extended_attributes = {}
383
384         children = node.GetChildren()
385         for child in children:
386             child_class = child.GetClass()
387             if child_class == 'Type':
388                 self.idl_type = type_node_to_type(child)
389             elif child_class == 'ExtAttributes':
390                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
391             else:
392                 raise ValueError('Unrecognized node class: %s' % child_class)
393
394
395 ################################################################################
396 # Constants
397 ################################################################################
398
399 class IdlConstant(TypedObject):
400     def __init__(self, idl_name, node):
401         children = node.GetChildren()
402         num_children = len(children)
403         if num_children < 2 or num_children > 3:
404             raise ValueError('Expected 2 or 3 children, got %s' % num_children)
405         type_node = children[0]
406         value_node = children[1]
407         value_node_class = value_node.GetClass()
408         if value_node_class != 'Value':
409             raise ValueError('Expected Value node, got %s' % value_node_class)
410
411         self.idl_name = idl_name
412         self.name = node.GetName()
413         # ConstType is more limited than Type, so subtree is smaller and
414         # we don't use the full type_node_to_type function.
415         self.idl_type = type_node_inner_to_type(type_node)
416         # FIXME: This code is unnecessarily complicated due to the rather
417         # inconsistent way the upstream IDL parser outputs default values.
418         # http://crbug.com/374178
419         if value_node.GetProperty('TYPE') == 'float':
420             self.value = value_node.GetProperty('VALUE')
421         else:
422             self.value = value_node.GetName()
423
424         if num_children == 3:
425             ext_attributes_node = children[2]
426             self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node)
427         else:
428             self.extended_attributes = {}
429
430
431 ################################################################################
432 # Literals
433 ################################################################################
434
435 class IdlLiteral(object):
436     def __init__(self, idl_type, value):
437         self.idl_type = idl_type
438         self.value = value
439         self.is_null = False
440
441     def __str__(self):
442         if self.idl_type == 'DOMString':
443             return 'String("%s")' % self.value
444         if self.idl_type == 'integer':
445             return '%d' % self.value
446         if self.idl_type == 'float':
447             return '%g' % self.value
448         if self.idl_type == 'boolean':
449             return 'true' if self.value else 'false'
450         raise ValueError('Unsupported literal type: %s' % self.idl_type)
451
452
453 class IdlLiteralNull(IdlLiteral):
454     def __init__(self):
455         self.idl_type = 'NULL'
456         self.value = None
457         self.is_null = True
458
459     def __str__(self):
460         return 'nullptr'
461
462
463 def default_node_to_idl_literal(node):
464     # FIXME: This code is unnecessarily complicated due to the rather
465     # inconsistent way the upstream IDL parser outputs default values.
466     # http://crbug.com/374178
467     idl_type = node.GetProperty('TYPE')
468     if idl_type == 'DOMString':
469         value = node.GetProperty('NAME')
470         if '"' in value or '\\' in value:
471             raise ValueError('Unsupported string value: %r' % value)
472         return IdlLiteral(idl_type, value)
473     if idl_type == 'integer':
474         return IdlLiteral(idl_type, int(node.GetProperty('NAME'), base=0))
475     if idl_type == 'float':
476         return IdlLiteral(idl_type, float(node.GetProperty('VALUE')))
477     if idl_type == 'boolean':
478         return IdlLiteral(idl_type, node.GetProperty('VALUE'))
479     if idl_type == 'NULL':
480         return IdlLiteralNull()
481     raise ValueError('Unrecognized default value type: %s' % idl_type)
482
483
484 ################################################################################
485 # Operations
486 ################################################################################
487
488 class IdlOperation(TypedObject):
489     def __init__(self, idl_name, node=None):
490         self.arguments = []
491         self.extended_attributes = {}
492         self.specials = []
493         self.is_constructor = False
494
495         if not node:
496             self.is_static = False
497             return
498         self.idl_name = idl_name
499         self.name = node.GetName()  # FIXME: should just be: or ''
500         # FIXME: AST should use None internally
501         if self.name == '_unnamed_':
502             self.name = ''
503
504         self.is_static = node.GetProperty('STATIC') or False
505         property_dictionary = node.GetProperties()
506         for special_keyword in SPECIAL_KEYWORD_LIST:
507             if special_keyword in property_dictionary:
508                 self.specials.append(special_keyword.lower())
509
510         self.idl_type = None
511         children = node.GetChildren()
512         for child in children:
513             child_class = child.GetClass()
514             if child_class == 'Arguments':
515                 self.arguments = arguments_node_to_arguments(idl_name, child)
516             elif child_class == 'Type':
517                 self.idl_type = type_node_to_type(child)
518             elif child_class == 'ExtAttributes':
519                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
520             else:
521                 raise ValueError('Unrecognized node class: %s' % child_class)
522
523     @classmethod
524     def from_exception_operation_node(cls, idl_name, node):
525         # Needed to handle one case in DOMException.idl:
526         # // Override in a Mozilla compatible format
527         # [NotEnumerable] DOMString toString();
528         # FIXME: can we remove this? replace with a stringifier?
529         operation = cls(idl_name)
530         operation.name = node.GetName()
531         children = node.GetChildren()
532         if len(children) < 1 or len(children) > 2:
533             raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children))
534
535         type_node = children[0]
536         operation.idl_type = type_node_to_type(type_node)
537
538         if len(children) > 1:
539             ext_attributes_node = children[1]
540             operation.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node)
541
542         return operation
543
544     @classmethod
545     def constructor_from_arguments_node(cls, name, idl_name, arguments_node):
546         constructor = cls(idl_name)
547         constructor.name = name
548         constructor.arguments = arguments_node_to_arguments(idl_name, arguments_node)
549         constructor.is_constructor = True
550         return constructor
551
552     def resolve_typedefs(self, typedefs):
553         TypedObject.resolve_typedefs(self, typedefs)
554         for argument in self.arguments:
555             argument.resolve_typedefs(typedefs)
556
557
558 ################################################################################
559 # Arguments
560 ################################################################################
561
562 class IdlArgument(TypedObject):
563     def __init__(self, idl_name, node):
564         self.extended_attributes = {}
565         self.idl_type = None
566         self.is_optional = node.GetProperty('OPTIONAL')  # syntax: (optional T)
567         self.is_variadic = False  # syntax: (T...)
568         self.idl_name = idl_name
569         self.name = node.GetName()
570         self.default_value = None
571
572         children = node.GetChildren()
573         for child in children:
574             child_class = child.GetClass()
575             if child_class == 'Type':
576                 self.idl_type = type_node_to_type(child)
577             elif child_class == 'ExtAttributes':
578                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
579             elif child_class == 'Argument':
580                 child_name = child.GetName()
581                 if child_name != '...':
582                     raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
583                 self.is_variadic = child.GetProperty('ELLIPSIS') or False
584             elif child_class == 'Default':
585                 self.default_value = default_node_to_idl_literal(child)
586             else:
587                 raise ValueError('Unrecognized node class: %s' % child_class)
588
589
590 def arguments_node_to_arguments(idl_name, node):
591     # [Constructor] and [CustomConstructor] without arguments (the bare form)
592     # have None instead of an arguments node, but have the same meaning as using
593     # an empty argument list, [Constructor()], so special-case this.
594     # http://www.w3.org/TR/WebIDL/#Constructor
595     if node is None:
596         return []
597     return [IdlArgument(idl_name, argument_node)
598             for argument_node in node.GetChildren()]
599
600
601 ################################################################################
602 # Stringifiers
603 ################################################################################
604
605 class IdlStringifier(object):
606     def __init__(self, idl_name, node):
607         self.attribute = None
608         self.operation = None
609         self.extended_attributes = {}
610         self.idl_name = idl_name
611
612         for child in node.GetChildren():
613             child_class = child.GetClass()
614             if child_class == 'Attribute':
615                 self.attribute = IdlAttribute(idl_name, child)
616             elif child_class == 'Operation':
617                 operation = IdlOperation(idl_name, child)
618                 if operation.name:
619                     self.operation = operation
620             elif child_class == 'ExtAttributes':
621                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
622             else:
623                 raise ValueError('Unrecognized node class: %s' % child_class)
624
625         # Copy the stringifier's extended attributes (such as [Unforgable]) onto
626         # the underlying attribute or operation, if there is one.
627         if self.attribute or self.operation:
628             (self.attribute or self.operation).extended_attributes.update(
629                 self.extended_attributes)
630
631
632 ################################################################################
633 # Extended attributes
634 ################################################################################
635
636 def ext_attributes_node_to_extended_attributes(idl_name, node):
637     """
638     Returns:
639       Dictionary of {ExtAttributeName: ExtAttributeValue}.
640       Value is usually a string, with these exceptions:
641       Constructors: value is a list of Arguments nodes, corresponding to
642         possible signatures of the constructor.
643       CustomConstructors: value is a list of Arguments nodes, corresponding to
644         possible signatures of the custom constructor.
645       NamedConstructor: value is a Call node, corresponding to the single
646         signature of the named constructor.
647       SetWrapperReferenceTo: value is an Arguments node.
648     """
649     # Primarily just make a dictionary from the children.
650     # The only complexity is handling various types of constructors:
651     # Constructors and Custom Constructors can have duplicate entries due to
652     # overloading, and thus are stored in temporary lists.
653     # However, Named Constructors cannot be overloaded, and thus do not have
654     # a list.
655     # FIXME: move Constructor logic into separate function, instead of modifying
656     #        extended attributes in-place.
657     constructors = []
658     custom_constructors = []
659     extended_attributes = {}
660
661     def child_node(extended_attribute_node):
662         children = extended_attribute_node.GetChildren()
663         if not children:
664             return None
665         if len(children) > 1:
666             raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
667         return children[0]
668
669     extended_attribute_node_list = node.GetChildren()
670     for extended_attribute_node in extended_attribute_node_list:
671         name = extended_attribute_node.GetName()
672         child = child_node(extended_attribute_node)
673         child_class = child and child.GetClass()
674         if name == 'Constructor':
675             if child_class and child_class != 'Arguments':
676                 raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
677             constructors.append(child)
678         elif name == 'CustomConstructor':
679             if child_class and child_class != 'Arguments':
680                 raise ValueError('[CustomConstructor] only supports Arguments as child, but has child of class: %s' % child_class)
681             custom_constructors.append(child)
682         elif name == 'NamedConstructor':
683             if child_class and child_class != 'Call':
684                 raise ValueError('[NamedConstructor] only supports Call as child, but has child of class: %s' % child_class)
685             extended_attributes[name] = child
686         elif name == 'SetWrapperReferenceTo':
687             if not child:
688                 raise ValueError('[SetWrapperReferenceTo] requires a child, but has none.')
689             if child_class != 'Arguments':
690                 raise ValueError('[SetWrapperReferenceTo] only supports Arguments as child, but has child of class: %s' % child_class)
691             extended_attributes[name] = arguments_node_to_arguments(idl_name, child)
692         elif child:
693             raise ValueError('ExtAttributes node with unexpected children: %s' % name)
694         else:
695             value = extended_attribute_node.GetProperty('VALUE')
696             extended_attributes[name] = value
697
698     # Store constructors and custom constructors in special list attributes,
699     # which are deleted later. Note plural in key.
700     if constructors:
701         extended_attributes['Constructors'] = constructors
702     if custom_constructors:
703         extended_attributes['CustomConstructors'] = custom_constructors
704
705     return extended_attributes
706
707
708 def extended_attributes_to_constructors(idl_name, extended_attributes):
709     """Returns constructors and custom_constructors (lists of IdlOperations).
710
711     Auxiliary function for IdlInterface.__init__.
712     """
713
714     constructor_list = extended_attributes.get('Constructors', [])
715     constructors = [
716         IdlOperation.constructor_from_arguments_node('Constructor', idl_name, arguments_node)
717         for arguments_node in constructor_list]
718
719     custom_constructor_list = extended_attributes.get('CustomConstructors', [])
720     custom_constructors = [
721         IdlOperation.constructor_from_arguments_node('CustomConstructor', idl_name, arguments_node)
722         for arguments_node in custom_constructor_list]
723
724     if 'NamedConstructor' in extended_attributes:
725         # FIXME: support overloaded named constructors, and make homogeneous
726         name = 'NamedConstructor'
727         call_node = extended_attributes['NamedConstructor']
728         extended_attributes['NamedConstructor'] = call_node.GetName()
729         children = call_node.GetChildren()
730         if len(children) != 1:
731             raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children))
732         arguments_node = children[0]
733         named_constructor = IdlOperation.constructor_from_arguments_node('NamedConstructor', idl_name, arguments_node)
734         # FIXME: should return named_constructor separately; appended for Perl
735         constructors.append(named_constructor)
736
737     return constructors, custom_constructors
738
739
740 def clear_constructor_attributes(extended_attributes):
741     # Deletes Constructor*s* (plural), sets Constructor (singular)
742     if 'Constructors' in extended_attributes:
743         del extended_attributes['Constructors']
744         extended_attributes['Constructor'] = None
745     if 'CustomConstructors' in extended_attributes:
746         del extended_attributes['CustomConstructors']
747         extended_attributes['CustomConstructor'] = None
748
749
750 ################################################################################
751 # Types
752 ################################################################################
753
754 def type_node_to_type(node):
755     children = node.GetChildren()
756     if len(children) < 1 or len(children) > 2:
757         raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
758
759     base_type = type_node_inner_to_type(children[0])
760
761     if node.GetProperty('NULLABLE'):
762         base_type = IdlNullableType(base_type)
763
764     if len(children) == 2:
765         array_node = children[1]
766         array_node_class = array_node.GetClass()
767         if array_node_class != 'Array':
768             raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
769         array_type = IdlArrayType(base_type)
770         if array_node.GetProperty('NULLABLE'):
771             return IdlNullableType(array_type)
772         return array_type
773
774     return base_type
775
776
777 def type_node_inner_to_type(node):
778     node_class = node.GetClass()
779     # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
780     # either a typedef shorthand (but not a Typedef declaration itself) or an
781     # interface type. We do not distinguish these, and just use the type name.
782     if node_class in ['PrimitiveType', 'Typeref']:
783         # unrestricted syntax: unrestricted double | unrestricted float
784         is_unrestricted = node.GetProperty('UNRESTRICTED') or False
785         return IdlType(node.GetName(), is_unrestricted=is_unrestricted)
786     elif node_class == 'Any':
787         return IdlType('any')
788     elif node_class == 'Sequence':
789         return sequence_node_to_type(node)
790     elif node_class == 'UnionType':
791         return union_type_node_to_idl_union_type(node)
792     raise ValueError('Unrecognized node class: %s' % node_class)
793
794
795 def sequence_node_to_type(node):
796     children = node.GetChildren()
797     if len(children) != 1:
798         raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
799     sequence_child = children[0]
800     sequence_child_class = sequence_child.GetClass()
801     if sequence_child_class != 'Type':
802         raise ValueError('Unrecognized node class: %s' % sequence_child_class)
803     element_type = type_node_to_type(sequence_child)
804     sequence_type = IdlSequenceType(element_type)
805     if node.GetProperty('NULLABLE'):
806         return IdlNullableType(sequence_type)
807     return sequence_type
808
809
810 def typedef_node_to_type(node):
811     children = node.GetChildren()
812     if len(children) != 1:
813         raise ValueError('Typedef node with %s children, expected 1' % len(children))
814     child = children[0]
815     child_class = child.GetClass()
816     if child_class != 'Type':
817         raise ValueError('Unrecognized node class: %s' % child_class)
818     return type_node_to_type(child)
819
820
821 def union_type_node_to_idl_union_type(node):
822     member_types = [type_node_to_type(member_type_node)
823                     for member_type_node in node.GetChildren()]
824     return IdlUnionType(member_types)