Index: client/dom/scripts/systemwrapping.py |
diff --git a/client/dom/scripts/systemwrapping.py b/client/dom/scripts/systemwrapping.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..53d79a5cbe66930c387abe44cbdf8ee497e32edb |
--- /dev/null |
+++ b/client/dom/scripts/systemwrapping.py |
@@ -0,0 +1,520 @@ |
+#!/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 shared functionality for the systems to generate |
+wrapping binding from the IDL database.""" |
+ |
+import os |
+from generator import * |
+from systembase import * |
+ |
+class WrappingImplementationSystem(System): |
+ |
+ def __init__(self, templates, database, emitters, output_dir): |
+ """Prepared for generating wrapping implementation. |
+ |
+ - Creates emitter for JS code. |
+ - Creates emitter for Dart code. |
+ """ |
+ super(WrappingImplementationSystem, self).__init__( |
+ templates, database, emitters, output_dir) |
+ self._dart_wrapping_file_paths = [] |
+ |
+ |
+ def InterfaceGenerator(self, |
+ interface, |
+ common_prefix, |
+ super_interface_name, |
+ source_filter): |
+ """.""" |
+ interface_name = interface.id |
+ dart_wrapping_file_path = self._FilePathForDartWrappingImpl(interface_name) |
+ |
+ self._dart_wrapping_file_paths.append(dart_wrapping_file_path) |
+ |
+ dart_code = self._emitters.FileEmitter(dart_wrapping_file_path) |
+ dart_code.Emit(self._templates.Load('wrapping_impl.darttemplate')) |
+ return WrappingInterfaceGenerator(interface, super_interface_name, |
+ dart_code, |
+ self._BaseDefines(interface)) |
+ |
+ def ProcessCallback(self, interface, info): |
+ pass |
+ |
+ def GenerateLibraries(self, lib_dir): |
+ # Library generated for implementation. |
+ self._GenerateLibFile( |
+ 'wrapping_dom.darttemplate', |
+ os.path.join(lib_dir, 'wrapping_dom.dart'), |
+ (self._interface_system._dart_interface_file_paths + |
+ self._interface_system._dart_callback_file_paths + |
+ # FIXME: Move the implementation to a separate library. |
+ self._dart_wrapping_file_paths |
+ )) |
+ |
+ |
+ def Finish(self): |
+ pass |
+ |
+ |
+ def _FilePathForDartWrappingImpl(self, interface_name): |
+ """Returns the file path of the Dart wrapping implementation.""" |
+ return os.path.join(self._output_dir, 'src', 'wrapping', |
+ '_%sWrappingImplementation.dart' % interface_name) |
+ |
+class WrappingInterfaceGenerator(object): |
+ """Generates Dart and JS implementation for one DOM IDL interface.""" |
+ |
+ def __init__(self, interface, super_interface, dart_code, base_members): |
+ """Generates Dart and JS 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. |
+ super_interface: A string or None, the name of the common interface that |
+ this interface implements, if any. |
+ dart_code: an Emitter for the file containing the Dart implementation |
+ class. |
+ base_members: a set of names of members defined in a base class. This is |
+ used to avoid static member 'overriding' in the generated Dart code. |
+ """ |
+ self._interface = interface |
+ self._super_interface = super_interface |
+ self._dart_code = dart_code |
+ self._base_members = base_members |
+ self._current_secondary_parent = None |
+ |
+ |
+ def StartInterface(self): |
+ interface = self._interface |
+ interface_name = interface.id |
+ |
+ self._class_name = self._ImplClassName(interface_name) |
+ |
+ base = self._BaseClassName(interface) |
+ |
+ (self._members_emitter, |
+ self._top_level_emitter) = self._dart_code.Emit( |
+ '\n' |
+ 'class $CLASS extends $BASE implements $INTERFACE {\n' |
+ ' $CLASS() : super() {}\n' |
+ '\n' |
+ ' static create_$CLASS() native {\n' |
+ ' return new $CLASS();\n' |
+ ' }\n' |
+ '$!MEMBERS' |
+ '\n' |
+ ' String get typeName() { return "$INTERFACE"; }\n' |
+ '}\n' |
+ '$!TOP_LEVEL', |
+ CLASS=self._class_name, BASE=base, INTERFACE=interface_name) |
+ |
+ def _ImplClassName(self, type_name): |
+ return '_' + type_name + 'WrappingImplementation' |
+ |
+ def _BaseClassName(self, interface): |
+ if not interface.parents: |
+ return 'DOMWrapperBase' |
+ |
+ supertype = interface.parents[0].type.id |
+ |
+ # FIXME: We're currently injecting List<..> and EventTarget as |
+ # supertypes in dart.idl. We should annotate/preserve as |
+ # attributes instead. For now, this hack lets the interfaces |
+ # inherit, but not the classes. |
+ # List methods are injected in AddIndexer. |
+ if IsDartListType(supertype) or IsDartCollectionType(supertype): |
+ return 'DOMWrapperBase' |
+ |
+ if supertype == 'EventTarget': |
+ # Most implementors of EventTarget specify the EventListener operations |
+ # again. If the operations are not specified, try to inherit from the |
+ # EventTarget implementation. |
+ # |
+ # Applies to MessagePort. |
+ if not [op for op in interface.operations if op.id == 'addEventListener']: |
+ return self._ImplClassName(supertype) |
+ return 'DOMWrapperBase' |
+ |
+ return self._ImplClassName(supertype) |
+ |
+ def FinishInterface(self): |
+ """.""" |
+ pass |
+ |
+ def AddConstant(self, constant): |
+ # Constants are already defined on the interface. |
+ pass |
+ |
+ def _MethodName(self, prefix, name): |
+ method_name = prefix + name |
+ if name in self._base_members: # Avoid illegal Dart 'static override'. |
+ method_name = method_name + '_' + self._interface.id |
+ return method_name |
+ |
+ def AddAttribute(self, getter, setter): |
+ if getter: |
+ self._AddGetter(getter) |
+ if setter: |
+ self._AddSetter(setter) |
+ |
+ def _AddGetter(self, attr): |
+ # FIXME: Instead of injecting the interface name into the method when it is |
+ # also implemented in the base class, suppress the method altogether if it |
+ # has the same signature. I.e., let the JS do the virtual dispatch instead. |
+ method_name = self._MethodName('_get_', attr.id) |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' $TYPE get $NAME() { return $METHOD(this); }\n' |
+ ' static $TYPE $METHOD(var _this) native;\n', |
+ NAME=attr.id, TYPE=attr.type.id, METHOD=method_name) |
+ |
+ def _AddSetter(self, attr): |
+ # FIXME: See comment on getter. |
+ method_name = self._MethodName('_set_', attr.id) |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' void set $NAME($TYPE value) { $METHOD(this, value); }\n' |
+ ' static void $METHOD(var _this, $TYPE value) native;\n', |
+ NAME=attr.id, TYPE=attr.type.id, METHOD=method_name) |
+ |
+ def AddSecondaryAttribute(self, interface, getter, setter): |
+ self._SecondaryContext(interface) |
+ self.AddAttribute(getter, setter) |
+ |
+ def AddSecondaryOperation(self, interface, info): |
+ self._SecondaryContext(interface) |
+ self.AddOperation(info) |
+ |
+ def AddEventAttributes(self, event_attrs): |
+ pass |
+ |
+ def _SecondaryContext(self, interface): |
+ if interface is not self._current_secondary_parent: |
+ self._current_secondary_parent = interface |
+ self._members_emitter.Emit('\n // From $WHERE\n', WHERE=interface.id) |
+ |
+ def AddIndexer(self, element_type): |
+ """Adds all the methods required to complete implementation of List.""" |
+ # We would like to simply inherit the implementation of everything except |
+ # get length(), [], and maybe []=. It is possible to extend from a base |
+ # array implementation class only when there is no other implementation |
+ # inheritance. There might be no implementation inheritance other than |
+ # DOMBaseWrapper for many classes, but there might be some where the |
+ # array-ness is introduced by a non-root interface: |
+ # |
+ # interface Y extends X, List<T> ... |
+ # |
+ # In the non-root case we have to choose between: |
+ # |
+ # class YImpl extends XImpl { add List<T> methods; } |
+ # |
+ # and |
+ # |
+ # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } |
+ # |
+ if self._HasNativeIndexGetter(self._interface): |
+ self._EmitNativeIndexGetter(self._interface, element_type) |
+ else: |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' $TYPE operator[](int index) {\n' |
+ ' return item(index);\n' |
+ ' }\n', |
+ TYPE=element_type) |
+ |
+ if self._HasNativeIndexSetter(self._interface): |
+ self._EmitNativeIndexSetter(self._interface, element_type) |
+ else: |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' void operator[]=(int index, $TYPE value) {\n' |
+ ' throw new UnsupportedOperationException("Cannot assign element of immutable List.");\n' |
+ ' }\n', |
+ TYPE=element_type) |
+ |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' void add($TYPE value) {\n' |
+ ' throw new UnsupportedOperationException("Cannot add to immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void addLast($TYPE value) {\n' |
+ ' throw new UnsupportedOperationException("Cannot add to immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void addAll(Collection<$TYPE> collection) {\n' |
+ ' throw new UnsupportedOperationException("Cannot add to immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void sort(int compare($TYPE a, $TYPE b)) {\n' |
+ ' throw new UnsupportedOperationException("Cannot sort immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void copyFrom(List<Object> src, int srcStart, ' |
+ 'int dstStart, int count) {\n' |
+ ' throw new UnsupportedOperationException("This object is immutable.");\n' |
+ ' }\n' |
+ '\n' |
+ ' int indexOf($TYPE element, [int start = 0]) {\n' |
+ ' return _Lists.indexOf(this, element, start, this.length);\n' |
+ ' }\n' |
+ '\n' |
+ ' int lastIndexOf($TYPE element, [int start = null]) {\n' |
+ ' if (start === null) start = length - 1;\n' |
+ ' return _Lists.lastIndexOf(this, element, start);\n' |
+ ' }\n' |
+ '\n' |
+ ' int clear() {\n' |
+ ' throw new UnsupportedOperationException("Cannot clear immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' $TYPE removeLast() {\n' |
+ ' throw new UnsupportedOperationException("Cannot removeLast on immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' $TYPE last() {\n' |
+ ' return this[length - 1];\n' |
+ ' }\n' |
+ '\n' |
+ ' void forEach(void f($TYPE element)) {\n' |
+ ' _Collections.forEach(this, f);\n' |
+ ' }\n' |
+ '\n' |
+ ' Collection map(f($TYPE element)) {\n' |
+ ' return _Collections.map(this, [], f);\n' |
+ ' }\n' |
+ '\n' |
+ ' Collection<$TYPE> filter(bool f($TYPE element)) {\n' |
+ ' return _Collections.filter(this, new List<$TYPE>(), f);\n' |
+ ' }\n' |
+ '\n' |
+ ' bool every(bool f($TYPE element)) {\n' |
+ ' return _Collections.every(this, f);\n' |
+ ' }\n' |
+ '\n' |
+ ' bool some(bool f($TYPE element)) {\n' |
+ ' return _Collections.some(this, f);\n' |
+ ' }\n' |
+ '\n' |
+ ' void setRange(int start, int length, List<$TYPE> from, [int startFrom]) {\n' |
+ ' throw new UnsupportedOperationException("Cannot setRange on immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void removeRange(int start, int length) {\n' |
+ ' throw new UnsupportedOperationException("Cannot removeRange on immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' void insertRange(int start, int length, [$TYPE initialValue]) {\n' |
+ ' throw new UnsupportedOperationException("Cannot insertRange on immutable List.");\n' |
+ ' }\n' |
+ '\n' |
+ ' List<$TYPE> getRange(int start, int length) {\n' |
+ ' throw new NotImplementedException();\n' |
+ ' }\n' |
+ '\n' |
+ ' bool isEmpty() {\n' |
+ ' return length == 0;\n' |
+ ' }\n' |
+ '\n' |
+ ' Iterator<$TYPE> iterator() {\n' |
+ ' return new _FixedSizeListIterator<$TYPE>(this);\n' |
+ ' }\n', |
+ TYPE=element_type) |
+ |
+ def _HasNativeIndexGetter(self, interface): |
+ return ('IndexedGetter' in interface.ext_attrs or |
+ 'NumericIndexedGetter' in interface.ext_attrs) |
+ |
+ def _EmitNativeIndexGetter(self, interface, element_type): |
+ method_name = '_index' |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' $TYPE operator[](int index) { return $METHOD(this, index); }\n' |
+ ' static $TYPE $METHOD(var _this, int index) native;\n', |
+ TYPE=element_type, METHOD=method_name) |
+ |
+ def _HasNativeIndexSetter(self, interface): |
+ return 'CustomIndexedSetter' in interface.ext_attrs |
+ |
+ def _EmitNativeIndexSetter(self, interface, element_type): |
+ method_name = '_set_index' |
+ self._members_emitter.Emit( |
+ '\n' |
+ ' void operator[]=(int index, $TYPE value) {\n' |
+ ' return $METHOD(this, index, value);\n' |
+ ' }\n' |
+ ' static $METHOD(_this, index, value) native;\n', |
+ TYPE=element_type, METHOD=method_name) |
+ |
+ def AddOperation(self, info): |
+ """ |
+ Arguments: |
+ info: An OperationInfo object. |
+ """ |
+ body = self._members_emitter.Emit( |
+ '\n' |
+ ' $TYPE $NAME($PARAMS) {\n' |
+ '$!BODY' |
+ ' }\n', |
+ TYPE=info.type_name, |
+ NAME=info.name, |
+ PARAMS=info.ParametersImplementationDeclaration()) |
+ |
+ # Process in order of ascending number of arguments to ensure missing |
+ # optional arguments are processed early. |
+ overloads = sorted(info.overloads, |
+ key=lambda overload: len(overload.arguments)) |
+ self._native_version = 0 |
+ fallthrough = self.GenerateDispatch(body, info, ' ', 0, overloads) |
+ if fallthrough: |
+ body.Emit(' throw "Incorrect number or type of arguments";\n'); |
+ |
+ def GenerateSingleOperation(self, emitter, info, indent, operation): |
+ """Generates a call to a single operation. |
+ |
+ Arguments: |
+ emitter: an Emitter for the body of a block of code. |
+ info: the compound information about the operation and its overloads. |
+ indent: an indentation string for generated code. |
+ operation: the IDLOperation to call. |
+ """ |
+ # TODO(sra): Do we need to distinguish calling with missing optional |
+ # arguments from passing 'null' which is represented as 'undefined'? |
+ def UnwrapArgExpression(name, type): |
+ # TODO: Type specific unwrapping. |
+ return '__dom_unwrap(%s)' % (name) |
+ |
+ def ArgNameAndUnwrapper(arg_info, overload_arg): |
+ (name, type, value) = arg_info |
+ return (name, UnwrapArgExpression(name, type)) |
+ |
+ names_and_unwrappers = [ArgNameAndUnwrapper(info.arg_infos[i], arg) |
+ for (i, arg) in enumerate(operation.arguments)] |
+ unwrap_args = [unwrap_arg for (_, unwrap_arg) in names_and_unwrappers] |
+ arg_names = [name for (name, _) in names_and_unwrappers] |
+ |
+ self._native_version += 1 |
+ native_name = self._MethodName('_', info.name) |
+ if self._native_version > 1: |
+ native_name = '%s_%s' % (native_name, self._native_version) |
+ |
+ argument_expressions = ', '.join(['this'] + arg_names) |
+ if info.type_name != 'void': |
+ emitter.Emit('$(INDENT)return $NATIVENAME($ARGS);\n', |
+ INDENT=indent, |
+ NATIVENAME=native_name, |
+ ARGS=argument_expressions) |
+ else: |
+ emitter.Emit('$(INDENT)$NATIVENAME($ARGS);\n' |
+ '$(INDENT)return;\n', |
+ INDENT=indent, |
+ NATIVENAME=native_name, |
+ ARGS=argument_expressions) |
+ |
+ self._members_emitter.Emit(' static $TYPE $NAME($PARAMS) native;\n', |
+ NAME=native_name, |
+ TYPE=info.type_name, |
+ PARAMS=', '.join(['receiver'] + arg_names) ) |
+ |
+ |
+ def GenerateDispatch(self, emitter, info, indent, position, overloads): |
+ """Generates a dispatch to one of the overloads. |
+ |
+ Arguments: |
+ emitter: an Emitter for the body of a block of code. |
+ info: the compound information about the operation and its overloads. |
+ indent: an indentation string for generated code. |
+ position: the index of the parameter to dispatch on. |
+ overloads: a list of the remaining IDLOperations to dispatch. |
+ |
+ Returns True if the dispatch can fall through on failure, False if the code |
+ always dispatches. |
+ """ |
+ |
+ def NullCheck(name): |
+ return '%s === null' % name |
+ |
+ def TypeCheck(name, type): |
+ return '%s is %s' % (name, type) |
+ |
+ if position == len(info.arg_infos): |
+ if len(overloads) > 1: |
+ raise Exception('Duplicate operations ' + str(overloads)) |
+ operation = overloads[0] |
+ self.GenerateSingleOperation(emitter, info, indent, operation) |
+ return False |
+ |
+ # FIXME: Consider a simpler dispatch that iterates over the |
+ # overloads and generates an overload specific check. Revisit |
+ # when we move to named optional arguments. |
+ |
+ # Partition the overloads to divide and conquer on the dispatch. |
+ positive = [] |
+ negative = [] |
+ first_overload = overloads[0] |
+ (param_name, param_type, param_default) = info.arg_infos[position] |
+ |
+ if position < len(first_overload.arguments): |
+ # FIXME: This will not work if the second overload has a more |
+ # precise type than the first. E.g., |
+ # void foo(Node x); |
+ # void foo(Element x); |
+ type = first_overload.arguments[position].type.id |
+ test = TypeCheck(param_name, type) |
+ pred = lambda op: len(op.arguments) > position and op.arguments[position].type.id == type |
+ else: |
+ type = None |
+ test = NullCheck(param_name) |
+ pred = lambda op: position >= len(op.arguments) |
+ |
+ for overload in overloads: |
+ if pred(overload): |
+ positive.append(overload) |
+ else: |
+ negative.append(overload) |
+ |
+ if positive and negative: |
+ (true_code, false_code) = emitter.Emit( |
+ '$(INDENT)if ($COND) {\n' |
+ '$!TRUE' |
+ '$(INDENT)} else {\n' |
+ '$!FALSE' |
+ '$(INDENT)}\n', |
+ COND=test, INDENT=indent) |
+ fallthrough1 = self.GenerateDispatch( |
+ true_code, info, indent + ' ', position + 1, positive) |
+ fallthrough2 = self.GenerateDispatch( |
+ false_code, info, indent + ' ', position, negative) |
+ return fallthrough1 or fallthrough2 |
+ |
+ if negative: |
+ raise Exception('Internal error, must be all positive') |
+ |
+ # All overloads require the same test. Do we bother? |
+ |
+ # If the test is the same as the method's formal parameter then checked mode |
+ # will have done the test already. (It could be null too but we ignore that |
+ # case since all the overload behave the same and we don't know which types |
+ # in the IDL are not nullable.) |
+ if type == param_type: |
+ return self.GenerateDispatch( |
+ emitter, info, indent, position + 1, positive) |
+ |
+ # Otherwise the overloads have the same type but the type is a substype of |
+ # the method's synthesized formal parameter. e.g we have overloads f(X) and |
+ # f(Y), implemented by the synthesized method f(Z) where X<Z and Y<Z. The |
+ # dispatch has removed f(X), leaving only f(Y), but there is no guarantee |
+ # that Y = Z-X, so we need to check for Y. |
+ true_code = emitter.Emit( |
+ '$(INDENT)if ($COND) {\n' |
+ '$!TRUE' |
+ '$(INDENT)}\n', |
+ COND=test, INDENT=indent) |
+ self.GenerateDispatch( |
+ true_code, info, indent + ' ', position + 1, positive) |
+ return True |