Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(887)

Side by Side Diff: client/dom/scripts/systembase.py

Issue 9403004: Wrapperless dart:html generator (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review fixes Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 base functionality for systems to generate
7 Dart APIs from the IDL database."""
8
9 import os
10 import re
11
12 # IDL->Dart primitive types conversion.
13 _idl_to_dart_type_conversions = {
14 'any': 'Object',
15 'any[]': 'List',
16 'custom': 'Dynamic',
17 'boolean': 'bool',
18 'DOMObject': 'Object',
19 'DOMString': 'String',
20 'DOMStringList': 'List<String>',
21 'DOMTimeStamp': 'int',
22 'Date': 'Date',
23 # Map to num to enable callers to pass in Dart int, rational
24 # types. Our implementations will need to convert these to
25 # doubles or floats as needed.
26 'double': 'num',
27 'float': 'num',
28 'int': 'int',
29 # Map to extra precision - int is a bignum in Dart.
30 'long': 'int',
31 'long long': 'int',
32 'object': 'Object',
33 # Map to extra precision - int is a bignum in Dart.
34 'short': 'int',
35 'string': 'String',
36 'void': 'void',
37 'Array': 'List',
38 'sequence': 'List',
39 # TODO(sra): Come up with some meaningful name so that where this appears in
40 # the documentation, the user is made aware that only a limited subset of
41 # serializable types are actually permitted.
42 'SerializedScriptValue': 'Dynamic',
43 # TODO(vsm): Automatically recognize types defined in src.
44 'TimeoutHandler': 'TimeoutHandler',
45 'RequestAnimationFrameCallback': 'RequestAnimationFrameCallback',
46
47 # TODO(sra): Flags is really a dictionary: {create:bool, exclusive:bool}
48 # http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#the-flags-interfa ce
49 'WebKitFlags': 'Object',
50 }
51
52 _dart_to_idl_type_conversions = dict((v,k) for k, v in
53 _idl_to_dart_type_conversions.iteritems())
54
55 #
56 # Identifiers that are used in the IDL than need to be treated specially because
57 # *some* JavaScript processors forbid them as properties.
58 #
59 _javascript_keywords = ['delete', 'continue']
60
61 #
62 # Interface version of the DOM needs to delegate typed array constructors to a
63 # factory provider.
64 #
65 interface_factories = {
66 'Float32Array': '_TypedArrayFactoryProvider',
67 'Float64Array': '_TypedArrayFactoryProvider',
68 'Int8Array': '_TypedArrayFactoryProvider',
69 'Int16Array': '_TypedArrayFactoryProvider',
70 'Int32Array': '_TypedArrayFactoryProvider',
71 'Uint8Array': '_TypedArrayFactoryProvider',
72 'Uint16Array': '_TypedArrayFactoryProvider',
73 'Uint32Array': '_TypedArrayFactoryProvider',
74 'Uint8ClampedArray': '_TypedArrayFactoryProvider',
75 }
76
77 #
78 # Custom methods that must be implemented by hand.
79 #
80 _custom_methods = set([
81 ('DOMWindow', 'setInterval'),
82 ('DOMWindow', 'setTimeout'),
83 ('WorkerContext', 'setInterval'),
84 ('WorkerContext', 'setTimeout'),
85 ('CanvasRenderingContext2D', 'setFillStyle'),
86 ('CanvasRenderingContext2D', 'setStrokeStyle'),
87 ('CanvasRenderingContext2D', 'setFillStyle'),
88 ])
89
90 #
91 # Custom getters that must be implemented by hand.
92 #
93 _custom_getters = set([
94 ('DOMWindow', 'localStorage'),
95 ])
96
97 #
98 # Custom native specs for the Frog dom.
99 #
100 _frog_dom_custom_native_specs = {
101 # Decorate the singleton Console object, if present (workers do not have a
102 # console).
103 'Console': "=(typeof console == 'undefined' ? {} : console)",
104
105 # DOMWindow aliased with global scope.
106 'DOMWindow': '@*DOMWindow',
107 }
108
109 #
110 # Simple method substitution when one method had different names on different
111 # browsers, but are otherwise identical. The alternates are tried in order and
112 # the first one defined is used.
113 #
114 # This can be probably be removed when Chrome renames initWebKitWheelEvent to
115 # initWheelEvent.
116 #
117 _alternate_methods = {
118 ('WheelEvent', 'initWheelEvent'): ['initWebKitWheelEvent', 'initWheelEvent']
119 }
120
121 def ConvertPrimitiveType(type_name):
122 if type_name.startswith('unsigned '):
123 type_name = type_name[len('unsigned '):]
124
125 if type_name in _idl_to_dart_type_conversions:
126 # Primitive type conversion
127 return _idl_to_dart_type_conversions[type_name]
128 return None
129
130 def IsPrimitiveType(type_name):
131 return (ConvertPrimitiveType(type_name) is not None or
132 type_name in _dart_to_idl_type_conversions)
133
134 def MaybeListElementTypeName(type_name):
135 """Returns the List element type T from string of form "List<T>", or None."""
136 match = re.match(r'List<(\w*)>$', type_name)
137 if match:
138 return match.group(1)
139 return None
140
141 def MaybeListElementType(interface):
142 """Returns the List element type T, or None in interface does not implement
143 List<T>.
144 """
145 for parent in interface.parents:
146 element_type = MaybeListElementTypeName(parent.type.id)
147 if element_type:
148 return element_type
149 return None
150
151 def MaybeTypedArrayElementType(interface):
152 """Returns the typed array element type, or None in interface is not a
153 TypedArray.
154 """
155 # Typed arrays implement ArrayBufferView and List<T>.
156 for parent in interface.parents:
157 if parent.type.id == 'ArrayBufferView':
158 return MaybeListElementType(interface)
159 if parent.type.id == 'Uint8Array':
160 return 'int'
161 return None
162
163 def MakeNativeSpec(javascript_binding_name):
164 if javascript_binding_name in _frog_dom_custom_native_specs:
165 return _frog_dom_custom_native_specs[javascript_binding_name]
166 else:
167 # Make the class 'hidden' so it is dynamically patched at runtime. This
168 # is useful not only for browser compat, but to allow code that links
169 # against dart:dom to load in a worker isolate.
170 return '*' + javascript_binding_name
171
172
173 def MatchSourceFilter(filter, thing):
174 if not filter:
175 return True
176 else:
177 return any(token in thing.annotations for token in filter)
178
179 def AnalyzeOperation(interface, operations):
180 """Makes operation calling convention decision for a set of overloads.
181
182 Returns: An OperationInfo object.
183 """
184
185 # Zip together arguments from each overload by position, then convert
186 # to a dart argument.
187
188 # Given a list of overloaded arguments, choose a suitable name.
189 def OverloadedName(args):
190 return '_OR_'.join(sorted(set(arg.id for arg in args)))
191
192 # Given a list of overloaded arguments, choose a suitable type.
193 def OverloadedType(args):
194 typeIds = sorted(set(arg.type.id for arg in args))
195 if len(typeIds) == 1:
196 return typeIds[0]
197 else:
198 return TypeName(typeIds, interface)
199
200 # Given a list of overloaded arguments, render a dart argument.
201 def DartArg(args):
202 filtered = filter(None, args)
203 optional = any(not arg or arg.is_optional for arg in args)
204 type = OverloadedType(filtered)
205 name = OverloadedName(filtered)
206 if optional:
207 return (name, type, 'null')
208 else:
209 return (name, type, None)
210
211 args = map(lambda *args: DartArg(args),
212 *(op.arguments for op in operations))
213
214 info = OperationInfo()
215 info.overloads = operations
216 info.declared_name = operations[0].id
217 info.name = operations[0].ext_attrs.get('DartName', info.declared_name)
218 info.js_name = info.declared_name
219 info.type_name = operations[0].type.id # TODO: widen.
220 info.arg_infos = args
221 return info
222
223 def RecognizeCallback(interface):
224 """Returns the info for the callback method if the interface smells like a
225 callback.
226 """
227 if 'Callback' not in interface.ext_attrs: return None
228 handlers = [op for op in interface.operations if op.id == 'handleEvent']
229 if not handlers: return None
230 if not (handlers == interface.operations): return None
231 return AnalyzeOperation(interface, handlers)
232
233 def IsDartListType(type):
234 return type == 'List' or type.startswith('List<')
235
236 def IsDartCollectionType(type):
237 return IsDartListType(type)
238
239 def FindMatchingAttribute(interface, attr1):
240 matches = [attr2 for attr2 in interface.attributes
241 if attr1.id == attr2.id
242 and attr1.is_fc_getter == attr2.is_fc_getter
243 and attr1.is_fc_setter == attr2.is_fc_setter]
244 if matches:
245 assert len(matches) == 1
246 return matches[0]
247 return None
248
249 class OperationInfo(object):
250 """Holder for various derived information from a set of overloaded operations.
251
252 Attributes:
253 overloads: A list of IDL operation overloads with the same name.
254 name: A string, the simple name of the operation.
255 type_name: A string, the name of the return type of the operation.
256 arg_infos: A list of (name, type, default_value) tuples.
257 default_value is None for mandatory arguments.
258 """
259
260 def ParametersInterfaceDeclaration(self):
261 """Returns a formatted string declaring the parameters for the interface."""
262 return self._FormatArgs(self.arg_infos, True)
263
264 def ParametersImplementationDeclaration(self, rename_type=None):
265 """Returns a formatted string declaring the parameters for the
266 implementation.
267
268 Args:
269 rename_type: A function that allows the types to be renamed.
270 """
271 args = self.arg_infos
272 if rename_type:
273 args = [(name, rename_type(type), default)
274 for (name, type, default) in args]
275 return self._FormatArgs(args, False)
276
277 def ParametersAsArgumentList(self):
278 """Returns a formatted string declaring the parameters names as an argument
279 list.
280 """
281 return ', '.join(map(lambda arg_info: arg_info[0], self.arg_infos))
282
283 def _FormatArgs(self, args, is_interface):
284 def FormatArg(arg_info):
285 """Returns an argument declaration fragment for an argument info tuple."""
286 (name, type, default) = arg_info
287 if default:
288 return '%s %s = %s' % (type, name, default)
289 else:
290 return '%s %s' % (type, name)
291
292 required = []
293 optional = []
294 for (name, type, default) in args:
295 if default:
296 if is_interface:
297 optional.append((name, type, None)) # Default values illegal.
298 else:
299 optional.append((name, type, default))
300 else:
301 if optional:
302 raise Exception('Optional arguments cannot precede required ones: '
303 + str(args))
304 required.append((name, type, None))
305 argtexts = map(FormatArg, required)
306 if optional:
307 argtexts.append('[' + ', '.join(map(FormatArg, optional)) + ']')
308 return ', '.join(argtexts)
309
310
311 def AttributeOutputOrder(a, b):
312 """Canonical output ordering for attributes."""
313 # Getters before setters:
314 if a.id < b.id: return -1
315 if a.id > b.id: return 1
316 if a.is_fc_setter < b.is_fc_setter: return -1
317 if a.is_fc_setter > b.is_fc_setter: return 1
318 return 0
319
320 def ConstantOutputOrder(a, b):
321 """Canonical output ordering for constants."""
322 if a.id < b.id: return -1
323 if a.id > b.id: return 1
324 return 0
325
326
327 def _FormatNameList(names):
328 """Returns JavaScript array literal expression with one name per line."""
329 #names = sorted(names)
330 if len(names) <= 1:
331 expression_string = str(names) # e.g. ['length']
332 else:
333 expression_string = ',\n '.join(str(names).split(','))
334 expression_string = expression_string.replace('[', '[\n ')
335 return expression_string
336
337
338 def IndentText(text, indent):
339 """Format lines of text with indent."""
340 def FormatLine(line):
341 if line.strip():
342 return '%s%s\n' % (indent, line)
343 else:
344 return '\n'
345 return ''.join(FormatLine(line) for line in text.split('\n'))
346
347 class System(object):
348 """Generates all the files for one implementation."""
349
350 def __init__(self, templates, database, emitters, output_dir):
351 self._templates = templates
352 self._database = database
353 self._emitters = emitters
354 self._output_dir = output_dir
355 self._dart_callback_file_paths = []
356
357 def InterfaceGenerator(self,
358 interface,
359 common_prefix,
360 super_interface_name,
361 source_filter):
362 """Returns an interface generator for |interface|."""
363 return None
364
365 def ProcessCallback(self, interface, info):
366 pass
367
368 def GenerateLibraries(self, lib_dir):
369 pass
370
371 def Finish(self):
372 pass
373
374
375 def _ProcessCallback(self, interface, info, file_path):
376 """Generates a typedef for the callback interface."""
377 self._dart_callback_file_paths.append(file_path)
378 code = self._emitters.FileEmitter(file_path)
379
380 code.Emit(self._templates.Load('callback.darttemplate'))
381 code.Emit('typedef $TYPE $NAME($PARAMS);\n',
382 NAME=interface.id,
383 TYPE=info.type_name,
384 PARAMS=info.ParametersImplementationDeclaration())
385
386 def _GenerateLibFile(self, lib_template, lib_file_path, file_paths,
387 **template_args):
388 """Generates a lib file from a template and a list of files.
389
390 Additional keyword arguments are passed to the template.
391 """
392 # Load template.
393 template = self._templates.Load(lib_template)
394 # Generate the .lib file.
395 lib_file_contents = self._emitters.FileEmitter(lib_file_path)
396
397 # Emit the list of #source directives.
398 list_emitter = lib_file_contents.Emit(template, **template_args)
399 lib_file_dir = os.path.dirname(lib_file_path)
400 for path in sorted(file_paths):
401 relpath = os.path.relpath(path, lib_file_dir)
402 list_emitter.Emit("#source('$PATH');\n", PATH=relpath)
403
404
405 def _BaseDefines(self, interface):
406 """Returns a set of names (strings) for members defined in a base class.
407 """
408 def WalkParentChain(interface):
409 if interface.parents:
410 # Only consider primary parent, secondary parents are not on the
411 # implementation class inheritance chain.
412 parent = interface.parents[0]
413 if IsDartCollectionType(parent.type.id):
414 return
415 if self._database.HasInterface(parent.type.id):
416 parent_interface = self._database.GetInterface(parent.type.id)
417 for attr in parent_interface.attributes:
418 result.add(attr.id)
419 for op in parent_interface.operations:
420 result.add(op.id)
421 WalkParentChain(parent_interface)
422
423 result = set()
424 WalkParentChain(interface)
425 return result;
426
427
428 # ------------------------------------------------------------------------------
429
430 class InterfacesSystem(System):
431
432 def __init__(self, templates, database, emitters, output_dir):
433 super(InterfacesSystem, self).__init__(
434 templates, database, emitters, output_dir)
435 self._dart_interface_file_paths = []
436
437
438 def InterfaceGenerator(self,
439 interface,
440 common_prefix,
441 super_interface_name,
442 source_filter):
443 """."""
444 interface_name = interface.id
445 dart_interface_file_path = self._FilePathForDartInterface(interface_name)
446
447 self._dart_interface_file_paths.append(dart_interface_file_path)
448
449 dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path)
450
451 template_file = 'interface_%s.darttemplate' % interface_name
452 template = self._templates.TryLoad(template_file)
453 if not template:
454 template = self._templates.Load('interface.darttemplate')
455
456 return DartInterfaceGenerator(
457 interface, dart_interface_code,
458 template,
459 common_prefix, super_interface_name,
460 source_filter)
461
462 def ProcessCallback(self, interface, info):
463 """Generates a typedef for the callback interface."""
464 interface_name = interface.id
465 file_path = self._FilePathForDartInterface(interface_name)
466 self._ProcessCallback(interface, info, file_path)
467
468 def GenerateLibraries(self, lib_dir):
469 pass
470
471
472 def _FilePathForDartInterface(self, interface_name):
473 """Returns the file path of the Dart interface definition."""
474 return os.path.join(self._output_dir, 'src', 'interface',
475 '%s.dart' % interface_name)
476
477 # ------------------------------------------------------------------------------
478
479 class DartInterfaceGenerator(object):
480 """Generates Dart Interface definition for one DOM IDL interface."""
481
482 def __init__(self, interface, emitter, template,
483 common_prefix, super_interface, source_filter):
484 """Generates Dart code for the given interface.
485
486 Args:
487 interface -- an IDLInterface instance. It is assumed that all types have
488 been converted to Dart types (e.g. int, String), unless they are in the
489 same package as the interface.
490 common_prefix -- the prefix for the common library, if any.
491 super_interface -- the name of the common interface that this interface
492 implements, if any.
493 source_filter -- if specified, rewrites the names of any superinterfaces
494 that are not from these sources to use the common prefix.
495 """
496 self._interface = interface
497 self._emitter = emitter
498 self._template = template
499 self._common_prefix = common_prefix
500 self._super_interface = super_interface
501 self._source_filter = source_filter
502
503
504 def StartInterface(self):
505 if self._super_interface:
506 typename = self._super_interface
507 else:
508 typename = self._interface.id
509
510
511 extends = []
512 suppressed_extends = []
513
514 for parent in self._interface.parents:
515 # TODO(vsm): Remove source_filter.
516 if MatchSourceFilter(self._source_filter, parent):
517 # Parent is a DOM type.
518 extends.append(parent.type.id)
519 elif '<' in parent.type.id:
520 # Parent is a Dart collection type.
521 # TODO(vsm): Make this check more robust.
522 extends.append(parent.type.id)
523 else:
524 suppressed_extends.append('%s.%s' %
525 (self._common_prefix, parent.type.id))
526
527 comment = ' extends'
528 extends_str = ''
529 if extends:
530 extends_str += ' extends ' + ', '.join(extends)
531 comment = ','
532 if suppressed_extends:
533 extends_str += ' /*%s %s */' % (comment, ', '.join(suppressed_extends))
534
535 if typename in interface_factories:
536 extends_str += ' default ' + interface_factories[typename]
537
538 # TODO(vsm): Add appropriate package / namespace syntax.
539 (self._members_emitter,
540 self._top_level_emitter) = self._emitter.Emit(
541 self._template + '$!TOP_LEVEL',
542 ID=typename,
543 EXTENDS=extends_str)
544
545 element_type = MaybeTypedArrayElementType(self._interface)
546 if element_type:
547 self._members_emitter.Emit(
548 '\n'
549 ' $CTOR(int length);\n'
550 '\n'
551 ' $CTOR.fromList(List<$TYPE> list);\n'
552 '\n'
553 ' $CTOR.fromBuffer(ArrayBuffer buffer);\n',
554 CTOR=self._interface.id,
555 TYPE=element_type)
556
557
558 def FinishInterface(self):
559 # TODO(vsm): Use typedef if / when that is supported in Dart.
560 # Define variant as subtype.
561 if (self._super_interface and
562 self._interface.id is not self._super_interface):
563 consts_emitter = self._top_level_emitter.Emit(
564 '\n'
565 'interface $NAME extends $BASE {\n'
566 '$!CONSTS'
567 '}\n',
568 NAME=self._interface.id,
569 BASE=self._super_interface)
570 for const in sorted(self._interface.constants, ConstantOutputOrder):
571 self._EmitConstant(consts_emitter, const)
572
573 def AddConstant(self, constant):
574 if (not self._super_interface or
575 self._interface.id is self._super_interface):
576 self._EmitConstant(self._members_emitter, constant)
577
578 def _EmitConstant(self, emitter, constant):
579 emitter.Emit('\n static final $TYPE $NAME = $VALUE;\n',
580 NAME=constant.id,
581 TYPE=constant.type.id,
582 VALUE=constant.value)
583
584 def AddAttribute(self, getter, setter):
585 if getter and setter and getter.type.id == setter.type.id:
586 self._members_emitter.Emit('\n $TYPE $NAME;\n',
587 NAME=getter.id, TYPE=getter.type.id);
588 return
589 if getter and not setter:
590 self._members_emitter.Emit('\n final $TYPE $NAME;\n',
591 NAME=getter.id, TYPE=getter.type.id);
592 return
593 raise Exception('Unexpected getter/setter combination %s %s' %
594 (getter, setter))
595
596 def AddIndexer(self, element_type):
597 # Interface inherits all operations from List<element_type>.
598 pass
599
600 def AddOperation(self, info):
601 """
602 Arguments:
603 operations - contains the overloads, one or more operations with the same
604 name.
605 """
606 self._members_emitter.Emit('\n'
607 ' $TYPE $NAME($PARAMS);\n',
608 TYPE=info.type_name,
609 NAME=info.name,
610 PARAMS=info.ParametersInterfaceDeclaration())
611
612 # Interfaces get secondary members directly via the superinterfaces.
613 def AddSecondaryAttribute(self, interface, getter, setter):
614 pass
615
616 def AddSecondaryOperation(self, interface, attr):
617 pass
618
619 def AddEventAttributes(self, event_attrs):
620 pass
621
622 # Given a sorted sequence of type identifiers, return an appropriate type
623 # name
624 def TypeName(typeIds, interface):
625 # Dynamically type this field for now.
626 return 'var'
627
628 # ------------------------------------------------------------------------------
629
630 class DummyInterfaceGenerator(object):
631 """Generates nothing."""
632
633 def __init__(self, system, interface):
634 pass
635
636 def StartInterface(self):
637 pass
638
639 def FinishInterface(self):
640 pass
641
642 def AddConstant(self, constant):
643 pass
644
645 def AddAttribute(self, getter, setter):
646 pass
647
648 def AddSecondaryAttribute(self, interface, getter, setter):
649 pass
650
651 def AddSecondaryOperation(self, interface, info):
652 pass
653
654 def AddIndexer(self, element_type):
655 pass
656
657 def AddTypedArrayConstructors(self, element_type):
658 pass
659
660 def AddOperation(self, info):
661 pass
662
663 def AddEventAttributes(self, event_attrs):
664 pass
665
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698