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 the system to generate | |
7 dart:html APIs from the IDL database.""" | |
8 | |
9 from generator import AnalyzeOperation, ConstantOutputOrder, \ | |
10 DartDomNameOfAttribute, FindMatchingAttribute, IsDartCollectionType, \ | |
11 IsPureInterface, TypeOrNothing | |
12 | |
13 # Types that are accessible cross-frame in a limited fashion. | |
14 # In these cases, the base type (e.g., WindowBase) provides restricted access | |
15 # while the subtype (e.g., Window) provides full access to the | |
16 # corresponding objects if there are from the same frame. | |
17 _secure_base_types = { | |
18 'Window': 'WindowBase', | |
19 'Location': 'LocationBase', | |
20 'History': 'HistoryBase', | |
21 } | |
22 | |
23 class HtmlDartGenerator(object): | |
24 def __init__(self, interface, options): | |
25 self._database = options.database | |
26 self._interface = interface | |
27 self._type_registry = options.type_registry | |
28 self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) | |
29 self._renamer = options.renamer | |
30 | |
31 def EmitAttributeDocumentation(self, attribute): | |
32 """ Emits the MDN dartdoc comment for an attribute. | |
33 """ | |
34 dom_name = DartDomNameOfAttribute(attribute) | |
35 self._members_emitter.Emit('\n /// @domName $DOMINTERFACE.$DOMNAME;' | |
36 ' @docsEditable true', | |
37 DOMINTERFACE=attribute.doc_js_interface_name, | |
38 DOMNAME=dom_name) | |
39 | |
40 def EmitOperationDocumentation(self, operation): | |
41 """ Emits the MDN dartdoc comment for an operation. | |
42 """ | |
43 self._members_emitter.Emit('\n /// @domName $DOMINTERFACE.$DOMNAME;' | |
44 ' @docsEditable true', | |
45 DOMINTERFACE=operation.overloads[0].doc_js_interface_name, | |
46 DOMNAME=operation.name) | |
47 | |
48 def EmitEventGetter(self, events_class_name): | |
49 self._members_emitter.Emit( | |
50 '\n /// @domName EventTarget.addEventListener, ' | |
51 'EventTarget.removeEventListener, EventTarget.dispatchEvent;' | |
52 ' @docsEditable true' | |
53 '\n $TYPE get on =>\n new $TYPE(this);\n', | |
54 TYPE=events_class_name) | |
55 | |
56 def AddMembers(self, interface, declare_only=False): | |
57 for const in sorted(interface.constants, ConstantOutputOrder): | |
58 self.AddConstant(const) | |
59 | |
60 for attr in sorted(interface.attributes, ConstantOutputOrder): | |
61 if attr.type.id != 'EventListener': | |
62 self.AddAttribute(attr, declare_only) | |
63 | |
64 # The implementation should define an indexer if the interface directly | |
65 # extends List. | |
66 element_type = None | |
67 requires_indexer = False | |
68 if self._interface_type_info.list_item_type(): | |
69 self.AddIndexer(self._interface_type_info.list_item_type()) | |
70 else: | |
71 for parent in self._database.Hierarchy(self._interface): | |
72 if parent == self._interface: | |
73 continue | |
74 parent_type_info = self._type_registry.TypeInfo(parent.id) | |
75 if parent_type_info.list_item_type(): | |
76 self.AmendIndexer(parent_type_info.list_item_type()) | |
77 break | |
78 | |
79 # Group overloaded operations by id. | |
80 operationsById = {} | |
81 for operation in interface.operations: | |
82 if operation.id not in operationsById: | |
83 operationsById[operation.id] = [] | |
84 operationsById[operation.id].append(operation) | |
85 | |
86 # Generate operations. | |
87 for id in sorted(operationsById.keys()): | |
88 operations = operationsById[id] | |
89 info = AnalyzeOperation(interface, operations) | |
90 self.AddOperation(info, declare_only) | |
91 | |
92 def AddSecondaryMembers(self, interface): | |
93 # With multiple inheritance, attributes and operations of non-first | |
94 # interfaces need to be added. Sometimes the attribute or operation is | |
95 # defined in the current interface as well as a parent. In that case we | |
96 # avoid making a duplicate definition and pray that the signatures match. | |
97 secondary_parents = self._TransitiveSecondaryParents(interface) | |
98 for parent_interface in sorted(secondary_parents): | |
99 if isinstance(parent_interface, str): | |
100 continue | |
101 for attr in sorted(parent_interface.attributes, ConstantOutputOrder): | |
102 if not FindMatchingAttribute(interface, attr): | |
103 self.SecondaryContext(parent_interface) | |
104 self.AddAttribute(attr) | |
105 | |
106 # Group overloaded operations by id. | |
107 operationsById = {} | |
108 for operation in parent_interface.operations: | |
109 if operation.id not in operationsById: | |
110 operationsById[operation.id] = [] | |
111 operationsById[operation.id].append(operation) | |
112 | |
113 # Generate operations. | |
114 for id in sorted(operationsById.keys()): | |
115 if not any(op.id == id for op in interface.operations): | |
116 operations = operationsById[id] | |
117 info = AnalyzeOperation(interface, operations) | |
118 self.SecondaryContext(parent_interface) | |
119 self.AddOperation(info) | |
120 | |
121 def AddConstant(self, constant): | |
122 const_name = self._renamer.RenameMember( | |
123 self._interface.id, constant, constant.id, dartify_name=False) | |
124 if not const_name: | |
125 return | |
126 type = TypeOrNothing(self._DartType(constant.type.id), constant.type.id) | |
127 self._members_emitter.Emit('\n static const $TYPE$NAME = $VALUE;\n', | |
128 NAME=const_name, | |
129 TYPE=type, | |
130 VALUE=constant.value) | |
131 | |
132 def AddAttribute(self, attribute, declare_only=False): | |
133 """ Adds an attribute to the generated class. | |
134 Arguments: | |
135 attribute - The attribute which is to be added. | |
136 declare_only- True if the attribute should be declared as an abstract | |
137 member and not include invocation code. | |
138 """ | |
139 dom_name = DartDomNameOfAttribute(attribute) | |
140 attr_name = self._renamer.RenameMember( | |
141 self._interface.id, attribute, dom_name, 'get:') | |
142 if not attr_name or self._IsPrivate(attr_name): | |
143 return | |
144 | |
145 html_setter_name = self._renamer.RenameMember( | |
146 self._interface.id, attribute, dom_name, 'set:') | |
147 read_only = (attribute.is_read_only or 'Replaceable' in attribute.ext_attrs | |
148 or not html_setter_name) | |
149 | |
150 # We don't yet handle inconsistent renames of the getter and setter yet. | |
151 assert(not html_setter_name or attr_name == html_setter_name) | |
152 | |
153 if declare_only: | |
154 self.DeclareAttribute(attribute, | |
155 self.SecureOutputType(attribute.type.id), attr_name, read_only) | |
156 else: | |
157 self.EmitAttribute(attribute, attr_name, read_only) | |
158 | |
159 def AddOperation(self, info, declare_only=False): | |
160 """ Adds an operation to the generated class. | |
161 Arguments: | |
162 info - The operation info of the operation to be added. | |
163 declare_only- True if the operation should be declared as an abstract | |
164 member and not include invocation code. | |
165 """ | |
166 # FIXME: When we pass in operations[0] below, we're assuming all | |
167 # overloaded operations have the same security attributes. This | |
168 # is currently true, but we should consider filtering earlier or | |
169 # merging the relevant data into info itself. | |
170 method_name = self._renamer.RenameMember(self._interface.id, | |
171 info.operations[0], | |
172 info.name) | |
173 if not method_name: | |
174 if info.name == 'item': | |
175 # FIXME: item should be renamed to operator[], not removed. | |
176 self.EmitOperation(info, '_item') | |
177 return | |
178 | |
179 if declare_only: | |
180 self.DeclareOperation(info, | |
181 self.SecureOutputType(info.type_name), method_name) | |
182 else: | |
183 self.EmitOperation(info, method_name) | |
184 | |
185 def AdditionalImplementedInterfaces(self): | |
186 # TODO: Include all implemented interfaces, including other Lists. | |
187 implements = [] | |
188 if self._interface_type_info.is_typed_array(): | |
189 element_type = self._interface_type_info.list_item_type() | |
190 implements.append('List<%s>' % self._DartType(element_type)) | |
191 if self._interface_type_info.list_item_type(): | |
192 item_type_info = self._type_registry.TypeInfo( | |
193 self._interface_type_info.list_item_type()) | |
194 implements.append('List<%s>' % item_type_info.dart_type()) | |
195 return implements | |
196 | |
197 def AddConstructors(self, | |
198 constructors, factory_name, factory_constructor_name): | |
199 """ Adds all of the constructors. | |
200 Arguments: | |
201 constructors - List of the constructors to be added. | |
202 factory_name - Name of the factory for this class. | |
203 factory_constructor_name - The name of the constructor on the | |
204 factory_name to call (calls an autogenerated FactoryProvider | |
205 if unspecified) | |
206 """ | |
207 for constructor_info in constructors: | |
208 self._AddConstructor( | |
209 constructor_info, factory_name, factory_constructor_name) | |
210 | |
211 typed_array_type = None | |
212 for interface in self._database.Hierarchy(self._interface): | |
213 type_info = self._type_registry.TypeInfo(interface.id) | |
214 if type_info.is_typed_array(): | |
215 typed_array_type = type_info.list_item_type() | |
216 break | |
217 if typed_array_type: | |
218 self._members_emitter.Emit( | |
219 '\n' | |
220 ' factory $CTOR(int length) =>\n' | |
221 ' $FACTORY.create$(CTOR)(length);\n' | |
222 '\n' | |
223 ' factory $CTOR.fromList(List<$TYPE> list) =>\n' | |
224 ' $FACTORY.create$(CTOR)_fromList(list);\n' | |
225 '\n' | |
226 ' factory $CTOR.fromBuffer(ArrayBuffer buffer, ' | |
227 '[int byteOffset, int length]) => \n' | |
228 ' $FACTORY.create$(CTOR)_fromBuffer(buffer, byteOffset, length);\n'
, | |
229 CTOR=self._interface.id, | |
230 TYPE=self._DartType(typed_array_type), | |
231 FACTORY=factory_name) | |
232 | |
233 def _AddConstructor(self, | |
234 constructor_info, factory_name, factory_constructor_name): | |
235 self._members_emitter.Emit('\n ///@docsEditable true'); | |
236 | |
237 if not factory_constructor_name: | |
238 factory_constructor_name = '_create' | |
239 factory_parameters = constructor_info.ParametersAsArgumentList() | |
240 has_factory_provider = True | |
241 else: | |
242 factory_parameters = ', '.join(constructor_info.factory_parameters) | |
243 has_factory_provider = False | |
244 | |
245 has_optional = any(param_info.is_optional | |
246 for param_info in constructor_info.param_infos) | |
247 | |
248 if not has_optional: | |
249 self._members_emitter.Emit( | |
250 '\n' | |
251 ' factory $CTOR($PARAMS) => ' | |
252 '$FACTORY.$CTOR_FACTORY_NAME($FACTORY_PARAMS);\n', | |
253 CTOR=constructor_info._ConstructorFullName(self._DartType), | |
254 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
255 FACTORY=factory_name, | |
256 CTOR_FACTORY_NAME=factory_constructor_name, | |
257 FACTORY_PARAMS=factory_parameters) | |
258 else: | |
259 if has_factory_provider: | |
260 dispatcher_emitter = self._members_emitter.Emit( | |
261 '\n' | |
262 ' factory $CTOR($PARAMS) {\n' | |
263 '$!DISPATCHER' | |
264 ' return $FACTORY._create($FACTORY_PARAMS);\n' | |
265 ' }\n', | |
266 CTOR=constructor_info._ConstructorFullName(self._DartType), | |
267 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
268 FACTORY=factory_name, | |
269 FACTORY_PARAMS=constructor_info.ParametersAsArgumentList()) | |
270 | |
271 for index, param_info in enumerate(constructor_info.param_infos): | |
272 if param_info.is_optional: | |
273 dispatcher_emitter.Emit( | |
274 ' if (!?$OPT_PARAM_NAME) {\n' | |
275 ' return $FACTORY._create($FACTORY_PARAMS);\n' | |
276 ' }\n', | |
277 OPT_PARAM_NAME=constructor_info.param_infos[index].name, | |
278 FACTORY=factory_name, | |
279 FACTORY_PARAMS=constructor_info.ParametersAsArgumentList(index)) | |
280 else: | |
281 inits = self._members_emitter.Emit( | |
282 '\n' | |
283 ' factory $CONSTRUCTOR($PARAMS) {\n' | |
284 ' var e = $FACTORY.$CTOR_FACTORY_NAME($FACTORY_PARAMS);\n' | |
285 '$!INITS' | |
286 ' return e;\n' | |
287 ' }\n', | |
288 CONSTRUCTOR=constructor_info._ConstructorFullName(self._DartType), | |
289 FACTORY=factory_name, | |
290 CTOR_FACTORY_NAME=factory_constructor_name, | |
291 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
292 FACTORY_PARAMS=factory_parameters) | |
293 | |
294 for index, param_info in enumerate(constructor_info.param_infos): | |
295 if param_info.is_optional: | |
296 inits.Emit(' if ($E != null) e.$E = $E;\n', | |
297 E=constructor_info.param_infos[index].name) | |
298 | |
299 if not constructor_info.pure_dart_constructor: | |
300 template_file = ('factoryprovider_%s.darttemplate' % self._interface.doc_j
s_name) | |
301 template = self._template_loader.TryLoad(template_file) | |
302 if template: | |
303 # There is a class specific factory. | |
304 # TODO(antonm): should move into the class template. | |
305 self._members_emitter.Emit(template) | |
306 else: | |
307 self.EmitStaticFactory(constructor_info) | |
308 | |
309 def EmitHelpers(self, base_class): | |
310 pass | |
311 | |
312 def DeclareAttribute(self, attribute, type_name, attr_name, read_only): | |
313 """ Declares an attribute but does not include the code to invoke it. | |
314 """ | |
315 self.EmitAttributeDocumentation(attribute) | |
316 if read_only: | |
317 template = '\n $TYPE get $NAME;\n' | |
318 else: | |
319 template = '\n $TYPE $NAME;\n' | |
320 | |
321 self._members_emitter.Emit(template, | |
322 NAME=attr_name, | |
323 TYPE=type_name) | |
324 | |
325 def DeclareOperation(self, operation, return_type_name, method_name): | |
326 """ Declares an operation but does not include the code to invoke it. | |
327 Arguments: | |
328 operation - The operation to be declared. | |
329 return_type_name - The name of the return type. | |
330 method_name - The name of the method. | |
331 """ | |
332 self.EmitOperationDocumentation(operation) | |
333 self._members_emitter.Emit( | |
334 '\n' | |
335 ' $TYPE $NAME($PARAMS);\n', | |
336 TYPE=return_type_name, | |
337 NAME=method_name, | |
338 PARAMS=operation.ParametersDeclaration(self._DartType)) | |
339 | |
340 def EmitListMixin(self, element_name): | |
341 # TODO(sra): Use separate mixins for mutable implementations of List<T>. | |
342 # TODO(sra): Use separate mixins for typed array implementations of List<T>. | |
343 template_file = 'immutable_list_mixin.darttemplate' | |
344 has_contains = any(op.id == 'contains' for op in self._interface.operations) | |
345 has_clear = any(op.id == 'clear' for op in self._interface.operations) | |
346 has_length = False | |
347 has_length_setter = False | |
348 for attr in self._interface.attributes: | |
349 if attr.id == 'length': | |
350 has_length = True | |
351 has_length_setter = not attr.is_read_only | |
352 | |
353 has_num_items = any(attr.id == 'numberOfItems' | |
354 for attr in self._interface.attributes) | |
355 | |
356 template = self._template_loader.Load( | |
357 template_file, | |
358 { | |
359 'DEFINE_CONTAINS': not has_contains, | |
360 'DEFINE_CLEAR': not has_clear, | |
361 'DEFINE_LENGTH_AS_NUM_ITEMS': not has_length and has_num_items, | |
362 'DEFINE_LENGTH_SETTER': not has_length_setter, | |
363 }) | |
364 self._members_emitter.Emit(template, E=element_name) | |
365 | |
366 def SecureOutputType(self, type_name, is_dart_type=False): | |
367 """ Converts the type name to the secure type name for return types. | |
368 """ | |
369 if is_dart_type: | |
370 dart_name = type_name | |
371 else: | |
372 dart_name = self._DartType(type_name) | |
373 # We only need to secure Window. Only local History and Location are | |
374 # returned in generated code. | |
375 assert(dart_name != 'HistoryBase' and dart_name != 'LocationBase') | |
376 if dart_name == 'Window': | |
377 return _secure_base_types[dart_name] | |
378 return dart_name | |
379 | |
380 def SecureBaseName(self, type_name): | |
381 if type_name in _secure_base_types: | |
382 return _secure_base_types[type_name] | |
383 | |
384 def _TransitiveSecondaryParents(self, interface): | |
385 """Returns a list of all non-primary parents. | |
386 | |
387 The list contains the interface objects for interfaces defined in the | |
388 database, and the name for undefined interfaces. | |
389 """ | |
390 def walk(parents): | |
391 for parent in parents: | |
392 parent_name = parent.type.id | |
393 if parent_name == 'EventTarget': | |
394 # Currently EventTarget is implemented as a mixin, not a proper | |
395 # super interface---ignore its members. | |
396 continue | |
397 if IsDartCollectionType(parent_name): | |
398 result.append(parent_name) | |
399 continue | |
400 if self._database.HasInterface(parent_name): | |
401 parent_interface = self._database.GetInterface(parent_name) | |
402 result.append(parent_interface) | |
403 walk(parent_interface.parents) | |
404 | |
405 result = [] | |
406 if interface.parents: | |
407 parent = interface.parents[0] | |
408 if IsPureInterface(parent.type.id): | |
409 walk(interface.parents) | |
410 else: | |
411 walk(interface.parents[1:]) | |
412 return result | |
413 | |
414 def _DartType(self, type_name): | |
415 return self._type_registry.DartType(type_name) | |
416 | |
417 def _IsPrivate(self, name): | |
418 return name.startswith('_') | |
OLD | NEW |