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 generates Dart APIs from the IDL database.""" | |
7 | |
8 import emitter | |
9 import idlnode | |
10 import logging | |
11 import os | |
12 import re | |
13 import shutil | |
14 from generator import * | |
15 | |
16 _logger = logging.getLogger('dartgenerator') | |
17 | |
18 def MergeNodes(node, other): | |
19 node.operations.extend(other.operations) | |
20 for attribute in other.attributes: | |
21 if not node.has_attribute(attribute): | |
22 node.attributes.append(attribute) | |
23 | |
24 node.constants.extend(other.constants) | |
25 | |
26 class DartGenerator(object): | |
27 """Utilities to generate Dart APIs and corresponding JavaScript.""" | |
28 | |
29 def __init__(self): | |
30 self._auxiliary_files = {} | |
31 self._dart_templates_re = re.compile(r'[\w.:]+<([\w\.<>:]+)>') | |
32 | |
33 def _StripModules(self, type_name): | |
34 return type_name.split('::')[-1] | |
35 | |
36 def _IsCompoundType(self, database, type_name): | |
37 if IsRegisteredType(type_name): | |
38 return True | |
39 | |
40 if type_name.endswith('?'): | |
41 return self._IsCompoundType(database, type_name[:-len('?')]) | |
42 | |
43 if type_name.endswith('[]'): | |
44 return self._IsCompoundType(database, type_name[:-len('[]')]) | |
45 | |
46 stripped_type_name = self._StripModules(type_name) | |
47 if database.HasInterface(stripped_type_name): | |
48 return True | |
49 | |
50 dart_template_match = self._dart_templates_re.match(type_name) | |
51 if dart_template_match: | |
52 # Dart templates | |
53 parent_type_name = type_name[0 : dart_template_match.start(1) - 1] | |
54 sub_type_name = dart_template_match.group(1) | |
55 return (self._IsCompoundType(database, parent_type_name) and | |
56 self._IsCompoundType(database, sub_type_name)) | |
57 return False | |
58 | |
59 def _IsDartType(self, type_name): | |
60 return '.' in type_name | |
61 | |
62 def LoadAuxiliary(self, auxiliary_dir): | |
63 def Visitor(_, dirname, names): | |
64 for name in names: | |
65 if name.endswith('.dart'): | |
66 name = name[0:-5] # strip off ".dart" | |
67 self._auxiliary_files[name] = os.path.join(dirname, name) | |
68 os.path.walk(auxiliary_dir, Visitor, None) | |
69 | |
70 def FilterMembersWithUnidentifiedTypes(self, database): | |
71 """Removes unidentified types. | |
72 | |
73 Removes constants, attributes, operations and parents with unidentified | |
74 types. | |
75 """ | |
76 | |
77 for interface in database.GetInterfaces(): | |
78 def IsIdentified(idl_node): | |
79 node_name = idl_node.id if idl_node.id else 'parent' | |
80 for idl_type in idl_node.all(idlnode.IDLType): | |
81 type_name = idl_type.id | |
82 if (type_name is not None and | |
83 self._IsCompoundType(database, type_name)): | |
84 continue | |
85 _logger.warn('removing %s in %s which has unidentified type %s' % | |
86 (node_name, interface.id, type_name)) | |
87 return False | |
88 return True | |
89 | |
90 interface.constants = filter(IsIdentified, interface.constants) | |
91 interface.attributes = filter(IsIdentified, interface.attributes) | |
92 interface.operations = filter(IsIdentified, interface.operations) | |
93 interface.parents = filter(IsIdentified, interface.parents) | |
94 | |
95 def FilterInterfaces(self, database, | |
96 and_annotations=[], | |
97 or_annotations=[], | |
98 exclude_displaced=[], | |
99 exclude_suppressed=[]): | |
100 """Filters a database to remove interfaces and members that are missing | |
101 annotations. | |
102 | |
103 The FremontCut IDLs use annotations to specify implementation | |
104 status in various platforms. For example, if a member is annotated | |
105 with @WebKit, this means that the member is supported by WebKit. | |
106 | |
107 Args: | |
108 database -- the database to filter | |
109 all_annotations -- a list of annotation names a member has to | |
110 have or it will be filtered. | |
111 or_annotations -- if a member has one of these annotations, it | |
112 won't be filtered even if it is missing some of the | |
113 all_annotations. | |
114 exclude_displaced -- if a member has this annotation and it | |
115 is marked as displaced it will always be filtered. | |
116 exclude_suppressed -- if a member has this annotation and it | |
117 is marked as suppressed it will always be filtered. | |
118 """ | |
119 | |
120 # Filter interfaces and members whose annotations don't match. | |
121 for interface in database.GetInterfaces(): | |
122 def HasAnnotations(idl_node): | |
123 """Utility for determining if an IDLNode has all | |
124 the required annotations""" | |
125 for a in exclude_displaced: | |
126 if (a in idl_node.annotations | |
127 and 'via' in idl_node.annotations[a]): | |
128 return False | |
129 for a in exclude_suppressed: | |
130 if (a in idl_node.annotations | |
131 and 'suppressed' in idl_node.annotations[a]): | |
132 return False | |
133 for a in or_annotations: | |
134 if a in idl_node.annotations: | |
135 return True | |
136 if and_annotations == []: | |
137 return False | |
138 for a in and_annotations: | |
139 if a not in idl_node.annotations: | |
140 return False | |
141 return True | |
142 | |
143 if HasAnnotations(interface): | |
144 interface.constants = filter(HasAnnotations, interface.constants) | |
145 interface.attributes = filter(HasAnnotations, interface.attributes) | |
146 interface.operations = filter(HasAnnotations, interface.operations) | |
147 interface.parents = filter(HasAnnotations, interface.parents) | |
148 else: | |
149 database.DeleteInterface(interface.id) | |
150 | |
151 # Ugly temporary hack | |
152 websocket_interface = database.GetInterface('WebSocket') | |
153 def make_object(**fields): | |
154 o = type('Anon', (object,), {})() | |
155 for k, v in fields.items(): setattr(o, k, v) | |
156 o.ext_attrs = {} | |
157 return o | |
158 arg = make_object(id = 'url', type = make_object(id = 'DOMString')) | |
159 websocket_interface.ext_attrs['Constructor'] = make_object(arguments = [ar
g]) | |
160 websocket_interface.ext_attrs['CustomConstructor'] = True | |
161 | |
162 self.FilterMembersWithUnidentifiedTypes(database) | |
163 | |
164 def Generate(self, database, super_database, generate_interface): | |
165 self._database = database | |
166 | |
167 # Collect interfaces | |
168 interfaces = [] | |
169 for interface in database.GetInterfaces(): | |
170 if not MatchSourceFilter(interface): | |
171 # Skip this interface since it's not present in the required source | |
172 _logger.info('Omitting interface - %s' % interface.id) | |
173 continue | |
174 interfaces.append(interface) | |
175 | |
176 # TODO(sra): Use this list of exception names to generate information to | |
177 # tell dart2js which exceptions can be passed from JS to Dart code. | |
178 exceptions = self._CollectExceptions(interfaces) | |
179 | |
180 # Render all interfaces into Dart and save them in files. | |
181 for interface in self._PreOrderInterfaces(interfaces): | |
182 interface_name = interface.id | |
183 auxiliary_file = self._auxiliary_files.get(interface_name) | |
184 if auxiliary_file is not None: | |
185 _logger.info('Skipping %s because %s exists' % ( | |
186 interface_name, auxiliary_file)) | |
187 continue | |
188 | |
189 _logger.info('Generating %s' % interface.id) | |
190 generate_interface(interface) | |
191 | |
192 def _PreOrderInterfaces(self, interfaces): | |
193 """Returns the interfaces in pre-order, i.e. parents first.""" | |
194 seen = set() | |
195 ordered = [] | |
196 def visit(interface): | |
197 if interface.id in seen: | |
198 return | |
199 seen.add(interface.id) | |
200 for parent in interface.parents: | |
201 if IsDartCollectionType(parent.type.id): | |
202 continue | |
203 if self._database.HasInterface(parent.type.id): | |
204 parent_interface = self._database.GetInterface(parent.type.id) | |
205 visit(parent_interface) | |
206 ordered.append(interface) | |
207 | |
208 for interface in interfaces: | |
209 visit(interface) | |
210 return ordered | |
211 | |
212 def _CollectExceptions(self, interfaces): | |
213 """Returns the names of all exception classes raised.""" | |
214 exceptions = set() | |
215 for interface in interfaces: | |
216 for attribute in interface.attributes: | |
217 if attribute.get_raises: | |
218 exceptions.add(attribute.get_raises.id) | |
219 if attribute.set_raises: | |
220 exceptions.add(attribute.set_raises.id) | |
221 for operation in interface.operations: | |
222 if operation.raises: | |
223 exceptions.add(operation.raises.id) | |
224 return exceptions | |
225 | |
226 | |
227 def FixEventTargets(self, database): | |
228 for interface in database.GetInterfaces(): | |
229 # Create fake EventTarget parent interface for interfaces that have | |
230 # 'EventTarget' extended attribute. | |
231 if 'EventTarget' in interface.ext_attrs: | |
232 ast = [('Annotation', [('Id', 'WebKit')]), | |
233 ('InterfaceType', ('ScopedName', 'EventTarget'))] | |
234 interface.parents.append(idlnode.IDLParentInterface(ast)) | |
235 | |
236 def AddMissingArguments(self, database): | |
237 ARG = idlnode.IDLArgument([('Type', ('ScopedName', 'object')), ('Id', 'arg')
]) | |
238 for interface in database.GetInterfaces(): | |
239 for operation in interface.operations: | |
240 if operation.ext_attrs.get('CallWith') == 'ScriptArguments|ScriptState': | |
241 operation.arguments.append(ARG) | |
OLD | NEW |