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

Unified 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 side-by-side diff with in-line comments
Download patch
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
+

Powered by Google App Engine
This is Rietveld 408576698