Index: client/dom/scripts/systembase.py |
diff --git a/client/dom/scripts/systembase.py b/client/dom/scripts/systembase.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bb1d92acfad6daee6f61ebf1078970f631b646ff |
--- /dev/null |
+++ b/client/dom/scripts/systembase.py |
@@ -0,0 +1,665 @@ |
+#!/usr/bin/python |
+# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+# for details. All rights reserved. Use of this source code is governed by a |
+# BSD-style license that can be found in the LICENSE file. |
+ |
+"""This module provides base functionality for systems to generate |
+Dart APIs from the IDL database.""" |
+ |
+import os |
+import re |
+ |
+# IDL->Dart primitive types conversion. |
+_idl_to_dart_type_conversions = { |
+ 'any': 'Object', |
+ 'any[]': 'List', |
+ 'custom': 'Dynamic', |
+ 'boolean': 'bool', |
+ 'DOMObject': 'Object', |
+ 'DOMString': 'String', |
+ 'DOMStringList': 'List<String>', |
+ 'DOMTimeStamp': 'int', |
+ 'Date': 'Date', |
+ # Map to num to enable callers to pass in Dart int, rational |
+ # types. Our implementations will need to convert these to |
+ # doubles or floats as needed. |
+ 'double': 'num', |
+ 'float': 'num', |
+ 'int': 'int', |
+ # Map to extra precision - int is a bignum in Dart. |
+ 'long': 'int', |
+ 'long long': 'int', |
+ 'object': 'Object', |
+ # Map to extra precision - int is a bignum in Dart. |
+ 'short': 'int', |
+ 'string': 'String', |
+ 'void': 'void', |
+ 'Array': 'List', |
+ 'sequence': 'List', |
+ # TODO(sra): Come up with some meaningful name so that where this appears in |
+ # the documentation, the user is made aware that only a limited subset of |
+ # serializable types are actually permitted. |
+ 'SerializedScriptValue': 'Dynamic', |
+ # TODO(vsm): Automatically recognize types defined in src. |
+ 'TimeoutHandler': 'TimeoutHandler', |
+ 'RequestAnimationFrameCallback': 'RequestAnimationFrameCallback', |
+ |
+ # TODO(sra): Flags is really a dictionary: {create:bool, exclusive:bool} |
+ # http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#the-flags-interface |
+ 'WebKitFlags': 'Object', |
+ } |
+ |
+_dart_to_idl_type_conversions = dict((v,k) for k, v in |
+ _idl_to_dart_type_conversions.iteritems()) |
+ |
+# |
+# Identifiers that are used in the IDL than need to be treated specially because |
+# *some* JavaScript processors forbid them as properties. |
+# |
+_javascript_keywords = ['delete', 'continue'] |
+ |
+# |
+# Interface version of the DOM needs to delegate typed array constructors to a |
+# factory provider. |
+# |
+interface_factories = { |
+ 'Float32Array': '_TypedArrayFactoryProvider', |
+ 'Float64Array': '_TypedArrayFactoryProvider', |
+ 'Int8Array': '_TypedArrayFactoryProvider', |
+ 'Int16Array': '_TypedArrayFactoryProvider', |
+ 'Int32Array': '_TypedArrayFactoryProvider', |
+ 'Uint8Array': '_TypedArrayFactoryProvider', |
+ 'Uint16Array': '_TypedArrayFactoryProvider', |
+ 'Uint32Array': '_TypedArrayFactoryProvider', |
+ 'Uint8ClampedArray': '_TypedArrayFactoryProvider', |
+} |
+ |
+# |
+# Custom methods that must be implemented by hand. |
+# |
+_custom_methods = set([ |
+ ('DOMWindow', 'setInterval'), |
+ ('DOMWindow', 'setTimeout'), |
+ ('WorkerContext', 'setInterval'), |
+ ('WorkerContext', 'setTimeout'), |
+ ('CanvasRenderingContext2D', 'setFillStyle'), |
+ ('CanvasRenderingContext2D', 'setStrokeStyle'), |
+ ('CanvasRenderingContext2D', 'setFillStyle'), |
+ ]) |
+ |
+# |
+# Custom getters that must be implemented by hand. |
+# |
+_custom_getters = set([ |
+ ('DOMWindow', 'localStorage'), |
+ ]) |
+ |
+# |
+# Custom native specs for the Frog dom. |
+# |
+_frog_dom_custom_native_specs = { |
+ # Decorate the singleton Console object, if present (workers do not have a |
+ # console). |
+ 'Console': "=(typeof console == 'undefined' ? {} : console)", |
+ |
+ # DOMWindow aliased with global scope. |
+ 'DOMWindow': '@*DOMWindow', |
+} |
+ |
+# |
+# Simple method substitution when one method had different names on different |
+# browsers, but are otherwise identical. The alternates are tried in order and |
+# the first one defined is used. |
+# |
+# This can be probably be removed when Chrome renames initWebKitWheelEvent to |
+# initWheelEvent. |
+# |
+_alternate_methods = { |
+ ('WheelEvent', 'initWheelEvent'): ['initWebKitWheelEvent', 'initWheelEvent'] |
+} |
+ |
+def ConvertPrimitiveType(type_name): |
+ if type_name.startswith('unsigned '): |
+ type_name = type_name[len('unsigned '):] |
+ |
+ if type_name in _idl_to_dart_type_conversions: |
+ # Primitive type conversion |
+ return _idl_to_dart_type_conversions[type_name] |
+ return None |
+ |
+def IsPrimitiveType(type_name): |
+ return (ConvertPrimitiveType(type_name) is not None or |
+ type_name in _dart_to_idl_type_conversions) |
+ |
+def MaybeListElementTypeName(type_name): |
+ """Returns the List element type T from string of form "List<T>", or None.""" |
+ match = re.match(r'List<(\w*)>$', type_name) |
+ if match: |
+ return match.group(1) |
+ return None |
+ |
+def MaybeListElementType(interface): |
+ """Returns the List element type T, or None in interface does not implement |
+ List<T>. |
+ """ |
+ for parent in interface.parents: |
+ element_type = MaybeListElementTypeName(parent.type.id) |
+ if element_type: |
+ return element_type |
+ return None |
+ |
+def MaybeTypedArrayElementType(interface): |
+ """Returns the typed array element type, or None in interface is not a |
+ TypedArray. |
+ """ |
+ # Typed arrays implement ArrayBufferView and List<T>. |
+ for parent in interface.parents: |
+ if parent.type.id == 'ArrayBufferView': |
+ return MaybeListElementType(interface) |
+ if parent.type.id == 'Uint8Array': |
+ return 'int' |
+ return None |
+ |
+def MakeNativeSpec(javascript_binding_name): |
+ if javascript_binding_name in _frog_dom_custom_native_specs: |
+ return _frog_dom_custom_native_specs[javascript_binding_name] |
+ else: |
+ # Make the class 'hidden' so it is dynamically patched at runtime. This |
+ # is useful not only for browser compat, but to allow code that links |
+ # against dart:dom to load in a worker isolate. |
+ return '*' + javascript_binding_name |
+ |
+ |
+def MatchSourceFilter(filter, thing): |
+ if not filter: |
+ return True |
+ else: |
+ return any(token in thing.annotations for token in filter) |
+ |
+def AnalyzeOperation(interface, operations): |
+ """Makes operation calling convention decision for a set of overloads. |
+ |
+ Returns: An OperationInfo object. |
+ """ |
+ |
+ # Zip together arguments from each overload by position, then convert |
+ # to a dart argument. |
+ |
+ # Given a list of overloaded arguments, choose a suitable name. |
+ def OverloadedName(args): |
+ return '_OR_'.join(sorted(set(arg.id for arg in args))) |
+ |
+ # Given a list of overloaded arguments, choose a suitable type. |
+ def OverloadedType(args): |
+ typeIds = sorted(set(arg.type.id for arg in args)) |
+ if len(typeIds) == 1: |
+ return typeIds[0] |
+ else: |
+ return TypeName(typeIds, interface) |
+ |
+ # Given a list of overloaded arguments, render a dart argument. |
+ def DartArg(args): |
+ filtered = filter(None, args) |
+ optional = any(not arg or arg.is_optional for arg in args) |
+ type = OverloadedType(filtered) |
+ name = OverloadedName(filtered) |
+ if optional: |
+ return (name, type, 'null') |
+ else: |
+ return (name, type, None) |
+ |
+ args = map(lambda *args: DartArg(args), |
+ *(op.arguments for op in operations)) |
+ |
+ info = OperationInfo() |
+ info.overloads = operations |
+ info.declared_name = operations[0].id |
+ info.name = operations[0].ext_attrs.get('DartName', info.declared_name) |
+ info.js_name = info.declared_name |
+ info.type_name = operations[0].type.id # TODO: widen. |
+ info.arg_infos = args |
+ return info |
+ |
+def RecognizeCallback(interface): |
+ """Returns the info for the callback method if the interface smells like a |
+ callback. |
+ """ |
+ if 'Callback' not in interface.ext_attrs: return None |
+ handlers = [op for op in interface.operations if op.id == 'handleEvent'] |
+ if not handlers: return None |
+ if not (handlers == interface.operations): return None |
+ return AnalyzeOperation(interface, handlers) |
+ |
+def IsDartListType(type): |
+ return type == 'List' or type.startswith('List<') |
+ |
+def IsDartCollectionType(type): |
+ return IsDartListType(type) |
+ |
+def FindMatchingAttribute(interface, attr1): |
+ matches = [attr2 for attr2 in interface.attributes |
+ if attr1.id == attr2.id |
+ and attr1.is_fc_getter == attr2.is_fc_getter |
+ and attr1.is_fc_setter == attr2.is_fc_setter] |
+ if matches: |
+ assert len(matches) == 1 |
+ return matches[0] |
+ return None |
+ |
+class OperationInfo(object): |
+ """Holder for various derived information from a set of overloaded operations. |
+ |
+ Attributes: |
+ overloads: A list of IDL operation overloads with the same name. |
+ name: A string, the simple name of the operation. |
+ type_name: A string, the name of the return type of the operation. |
+ arg_infos: A list of (name, type, default_value) tuples. |
+ default_value is None for mandatory arguments. |
+ """ |
+ |
+ def ParametersInterfaceDeclaration(self): |
+ """Returns a formatted string declaring the parameters for the interface.""" |
+ return self._FormatArgs(self.arg_infos, True) |
+ |
+ def ParametersImplementationDeclaration(self, rename_type=None): |
+ """Returns a formatted string declaring the parameters for the |
+ implementation. |
+ |
+ Args: |
+ rename_type: A function that allows the types to be renamed. |
+ """ |
+ args = self.arg_infos |
+ if rename_type: |
+ args = [(name, rename_type(type), default) |
+ for (name, type, default) in args] |
+ return self._FormatArgs(args, False) |
+ |
+ def ParametersAsArgumentList(self): |
+ """Returns a formatted string declaring the parameters names as an argument |
+ list. |
+ """ |
+ return ', '.join(map(lambda arg_info: arg_info[0], self.arg_infos)) |
+ |
+ def _FormatArgs(self, args, is_interface): |
+ def FormatArg(arg_info): |
+ """Returns an argument declaration fragment for an argument info tuple.""" |
+ (name, type, default) = arg_info |
+ if default: |
+ return '%s %s = %s' % (type, name, default) |
+ else: |
+ return '%s %s' % (type, name) |
+ |
+ required = [] |
+ optional = [] |
+ for (name, type, default) in args: |
+ if default: |
+ if is_interface: |
+ optional.append((name, type, None)) # Default values illegal. |
+ else: |
+ optional.append((name, type, default)) |
+ else: |
+ if optional: |
+ raise Exception('Optional arguments cannot precede required ones: ' |
+ + str(args)) |
+ required.append((name, type, None)) |
+ argtexts = map(FormatArg, required) |
+ if optional: |
+ argtexts.append('[' + ', '.join(map(FormatArg, optional)) + ']') |
+ return ', '.join(argtexts) |
+ |
+ |
+def AttributeOutputOrder(a, b): |
+ """Canonical output ordering for attributes.""" |
+ # Getters before setters: |
+ if a.id < b.id: return -1 |
+ if a.id > b.id: return 1 |
+ if a.is_fc_setter < b.is_fc_setter: return -1 |
+ if a.is_fc_setter > b.is_fc_setter: return 1 |
+ return 0 |
+ |
+def ConstantOutputOrder(a, b): |
+ """Canonical output ordering for constants.""" |
+ if a.id < b.id: return -1 |
+ if a.id > b.id: return 1 |
+ return 0 |
+ |
+ |
+def _FormatNameList(names): |
+ """Returns JavaScript array literal expression with one name per line.""" |
+ #names = sorted(names) |
+ if len(names) <= 1: |
+ expression_string = str(names) # e.g. ['length'] |
+ else: |
+ expression_string = ',\n '.join(str(names).split(',')) |
+ expression_string = expression_string.replace('[', '[\n ') |
+ return expression_string |
+ |
+ |
+def IndentText(text, indent): |
+ """Format lines of text with indent.""" |
+ def FormatLine(line): |
+ if line.strip(): |
+ return '%s%s\n' % (indent, line) |
+ else: |
+ return '\n' |
+ return ''.join(FormatLine(line) for line in text.split('\n')) |
+ |
+class System(object): |
+ """Generates all the files for one implementation.""" |
+ |
+ def __init__(self, templates, database, emitters, output_dir): |
+ self._templates = templates |
+ self._database = database |
+ self._emitters = emitters |
+ self._output_dir = output_dir |
+ self._dart_callback_file_paths = [] |
+ |
+ def InterfaceGenerator(self, |
+ interface, |
+ common_prefix, |
+ super_interface_name, |
+ source_filter): |
+ """Returns an interface generator for |interface|.""" |
+ return None |
+ |
+ def ProcessCallback(self, interface, info): |
+ pass |
+ |
+ def GenerateLibraries(self, lib_dir): |
+ pass |
+ |
+ def Finish(self): |
+ pass |
+ |
+ |
+ def _ProcessCallback(self, interface, info, file_path): |
+ """Generates a typedef for the callback interface.""" |
+ self._dart_callback_file_paths.append(file_path) |
+ code = self._emitters.FileEmitter(file_path) |
+ |
+ code.Emit(self._templates.Load('callback.darttemplate')) |
+ code.Emit('typedef $TYPE $NAME($PARAMS);\n', |
+ NAME=interface.id, |
+ TYPE=info.type_name, |
+ PARAMS=info.ParametersImplementationDeclaration()) |
+ |
+ def _GenerateLibFile(self, lib_template, lib_file_path, file_paths, |
+ **template_args): |
+ """Generates a lib file from a template and a list of files. |
+ |
+ Additional keyword arguments are passed to the template. |
+ """ |
+ # Load template. |
+ template = self._templates.Load(lib_template) |
+ # Generate the .lib file. |
+ lib_file_contents = self._emitters.FileEmitter(lib_file_path) |
+ |
+ # Emit the list of #source directives. |
+ list_emitter = lib_file_contents.Emit(template, **template_args) |
+ lib_file_dir = os.path.dirname(lib_file_path) |
+ for path in sorted(file_paths): |
+ relpath = os.path.relpath(path, lib_file_dir) |
+ list_emitter.Emit("#source('$PATH');\n", PATH=relpath) |
+ |
+ |
+ def _BaseDefines(self, interface): |
+ """Returns a set of names (strings) for members defined in a base class. |
+ """ |
+ def WalkParentChain(interface): |
+ if interface.parents: |
+ # Only consider primary parent, secondary parents are not on the |
+ # implementation class inheritance chain. |
+ parent = interface.parents[0] |
+ if IsDartCollectionType(parent.type.id): |
+ return |
+ if self._database.HasInterface(parent.type.id): |
+ parent_interface = self._database.GetInterface(parent.type.id) |
+ for attr in parent_interface.attributes: |
+ result.add(attr.id) |
+ for op in parent_interface.operations: |
+ result.add(op.id) |
+ WalkParentChain(parent_interface) |
+ |
+ result = set() |
+ WalkParentChain(interface) |
+ return result; |
+ |
+ |
+# ------------------------------------------------------------------------------ |
+ |
+class InterfacesSystem(System): |
+ |
+ def __init__(self, templates, database, emitters, output_dir): |
+ super(InterfacesSystem, self).__init__( |
+ templates, database, emitters, output_dir) |
+ self._dart_interface_file_paths = [] |
+ |
+ |
+ def InterfaceGenerator(self, |
+ interface, |
+ common_prefix, |
+ super_interface_name, |
+ source_filter): |
+ """.""" |
+ interface_name = interface.id |
+ dart_interface_file_path = self._FilePathForDartInterface(interface_name) |
+ |
+ self._dart_interface_file_paths.append(dart_interface_file_path) |
+ |
+ dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path) |
+ |
+ template_file = 'interface_%s.darttemplate' % interface_name |
+ template = self._templates.TryLoad(template_file) |
+ if not template: |
+ template = self._templates.Load('interface.darttemplate') |
+ |
+ return DartInterfaceGenerator( |
+ interface, dart_interface_code, |
+ template, |
+ common_prefix, super_interface_name, |
+ source_filter) |
+ |
+ def ProcessCallback(self, interface, info): |
+ """Generates a typedef for the callback interface.""" |
+ interface_name = interface.id |
+ file_path = self._FilePathForDartInterface(interface_name) |
+ self._ProcessCallback(interface, info, file_path) |
+ |
+ def GenerateLibraries(self, lib_dir): |
+ pass |
+ |
+ |
+ def _FilePathForDartInterface(self, interface_name): |
+ """Returns the file path of the Dart interface definition.""" |
+ return os.path.join(self._output_dir, 'src', 'interface', |
+ '%s.dart' % interface_name) |
+ |
+# ------------------------------------------------------------------------------ |
+ |
+class DartInterfaceGenerator(object): |
+ """Generates Dart Interface definition for one DOM IDL interface.""" |
+ |
+ def __init__(self, interface, emitter, template, |
+ common_prefix, super_interface, source_filter): |
+ """Generates Dart code for the given interface. |
+ |
+ Args: |
+ interface -- an IDLInterface instance. It is assumed that all types have |
+ been converted to Dart types (e.g. int, String), unless they are in the |
+ same package as the interface. |
+ common_prefix -- the prefix for the common library, if any. |
+ super_interface -- the name of the common interface that this interface |
+ implements, if any. |
+ source_filter -- if specified, rewrites the names of any superinterfaces |
+ that are not from these sources to use the common prefix. |
+ """ |
+ self._interface = interface |
+ self._emitter = emitter |
+ self._template = template |
+ self._common_prefix = common_prefix |
+ self._super_interface = super_interface |
+ self._source_filter = source_filter |
+ |
+ |
+ def StartInterface(self): |
+ if self._super_interface: |
+ typename = self._super_interface |
+ else: |
+ typename = self._interface.id |
+ |
+ |
+ extends = [] |
+ suppressed_extends = [] |
+ |
+ for parent in self._interface.parents: |
+ # TODO(vsm): Remove source_filter. |
+ if MatchSourceFilter(self._source_filter, parent): |
+ # Parent is a DOM type. |
+ extends.append(parent.type.id) |
+ elif '<' in parent.type.id: |
+ # Parent is a Dart collection type. |
+ # TODO(vsm): Make this check more robust. |
+ extends.append(parent.type.id) |
+ else: |
+ suppressed_extends.append('%s.%s' % |
+ (self._common_prefix, parent.type.id)) |
+ |
+ comment = ' extends' |
+ extends_str = '' |
+ if extends: |
+ extends_str += ' extends ' + ', '.join(extends) |
+ comment = ',' |
+ if suppressed_extends: |
+ extends_str += ' /*%s %s */' % (comment, ', '.join(suppressed_extends)) |
+ |
+ if typename in interface_factories: |
+ extends_str += ' default ' + interface_factories[typename] |
+ |
+ # TODO(vsm): Add appropriate package / namespace syntax. |
+ (self._members_emitter, |
+ self._top_level_emitter) = self._emitter.Emit( |
+ self._template + '$!TOP_LEVEL', |
+ ID=typename, |
+ EXTENDS=extends_str) |
+ |
+ element_type = MaybeTypedArrayElementType(self._interface) |
+ if element_type: |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' $CTOR(int length);\n' |
+ '\n' |
+ ' $CTOR.fromList(List<$TYPE> list);\n' |
+ '\n' |
+ ' $CTOR.fromBuffer(ArrayBuffer buffer);\n', |
+ CTOR=self._interface.id, |
+ TYPE=element_type) |
+ |
+ |
+ def FinishInterface(self): |
+ # TODO(vsm): Use typedef if / when that is supported in Dart. |
+ # Define variant as subtype. |
+ if (self._super_interface and |
+ self._interface.id is not self._super_interface): |
+ consts_emitter = self._top_level_emitter.Emit( |
+ '\n' |
+ 'interface $NAME extends $BASE {\n' |
+ '$!CONSTS' |
+ '}\n', |
+ NAME=self._interface.id, |
+ BASE=self._super_interface) |
+ for const in sorted(self._interface.constants, ConstantOutputOrder): |
+ self._EmitConstant(consts_emitter, const) |
+ |
+ def AddConstant(self, constant): |
+ if (not self._super_interface or |
+ self._interface.id is self._super_interface): |
+ self._EmitConstant(self._members_emitter, constant) |
+ |
+ def _EmitConstant(self, emitter, constant): |
+ emitter.Emit('\n static final $TYPE $NAME = $VALUE;\n', |
+ NAME=constant.id, |
+ TYPE=constant.type.id, |
+ VALUE=constant.value) |
+ |
+ def AddAttribute(self, getter, setter): |
+ if getter and setter and getter.type.id == setter.type.id: |
+ self._members_emitter.Emit('\n $TYPE $NAME;\n', |
+ NAME=getter.id, TYPE=getter.type.id); |
+ return |
+ if getter and not setter: |
+ self._members_emitter.Emit('\n final $TYPE $NAME;\n', |
+ NAME=getter.id, TYPE=getter.type.id); |
+ return |
+ raise Exception('Unexpected getter/setter combination %s %s' % |
+ (getter, setter)) |
+ |
+ def AddIndexer(self, element_type): |
+ # Interface inherits all operations from List<element_type>. |
+ pass |
+ |
+ def AddOperation(self, info): |
+ """ |
+ Arguments: |
+ operations - contains the overloads, one or more operations with the same |
+ name. |
+ """ |
+ self._members_emitter.Emit('\n' |
+ ' $TYPE $NAME($PARAMS);\n', |
+ TYPE=info.type_name, |
+ NAME=info.name, |
+ PARAMS=info.ParametersInterfaceDeclaration()) |
+ |
+ # Interfaces get secondary members directly via the superinterfaces. |
+ def AddSecondaryAttribute(self, interface, getter, setter): |
+ pass |
+ |
+ def AddSecondaryOperation(self, interface, attr): |
+ pass |
+ |
+ def AddEventAttributes(self, event_attrs): |
+ pass |
+ |
+# Given a sorted sequence of type identifiers, return an appropriate type |
+# name |
+def TypeName(typeIds, interface): |
+ # Dynamically type this field for now. |
+ return 'var' |
+ |
+# ------------------------------------------------------------------------------ |
+ |
+class DummyInterfaceGenerator(object): |
+ """Generates nothing.""" |
+ |
+ def __init__(self, system, interface): |
+ pass |
+ |
+ def StartInterface(self): |
+ pass |
+ |
+ def FinishInterface(self): |
+ pass |
+ |
+ def AddConstant(self, constant): |
+ pass |
+ |
+ def AddAttribute(self, getter, setter): |
+ pass |
+ |
+ def AddSecondaryAttribute(self, interface, getter, setter): |
+ pass |
+ |
+ def AddSecondaryOperation(self, interface, info): |
+ pass |
+ |
+ def AddIndexer(self, element_type): |
+ pass |
+ |
+ def AddTypedArrayConstructors(self, element_type): |
+ pass |
+ |
+ def AddOperation(self, info): |
+ pass |
+ |
+ def AddEventAttributes(self, event_attrs): |
+ pass |
+ |