Index: sdk/lib/html/scripts/htmldartgenerator.py |
diff --git a/sdk/lib/html/scripts/htmldartgenerator.py b/sdk/lib/html/scripts/htmldartgenerator.py |
index a199963ba72c5cd4febeca4549ec093c7f990f7f..0dd7ab7ae7f89b879efcbdf8226a3cd00e03f5e4 100644 |
--- a/sdk/lib/html/scripts/htmldartgenerator.py |
+++ b/sdk/lib/html/scripts/htmldartgenerator.py |
@@ -6,23 +6,170 @@ |
"""This module provides shared functionality for the system to generate |
dart:html APIs from the IDL database.""" |
-from generator import DartDomNameOfAttribute |
+from generator import AnalyzeOperation, ConstantOutputOrder, \ |
+ DartDomNameOfAttribute, FindMatchingAttribute, IsDartCollectionType, \ |
+ IsPureInterface |
+ |
+# Types that are accessible cross-frame in a limited fashion. |
+# In these cases, the base type (e.g., Window) provides restricted access |
+# while the subtype (e.g., LocalWindow) provides full access to the |
+# corresponding objects if there are from the same frame. |
+_secure_base_types = { |
+ 'LocalWindow': 'Window', |
+ 'LocalLocation': 'Location', |
+ 'LocalHistory': 'History', |
+} |
class HtmlDartGenerator(object): |
def __init__(self, interface, options): |
+ self._database = options.database |
self._interface = interface |
+ self._type_registry = options.type_registry |
+ self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) |
+ self._renamer = options.renamer |
def EmitAttributeDocumentation(self, attribute): |
+ """ Emits the MDN dartdoc comment for an attribute. |
+ """ |
dom_name = DartDomNameOfAttribute(attribute) |
self._members_emitter.Emit('\n /** @domName $DOMINTERFACE.$DOMNAME */', |
DOMINTERFACE=attribute.doc_js_interface_name, |
DOMNAME=dom_name) |
def EmitOperationDocumentation(self, operation): |
+ """ Emits the MDN dartdoc comment for an operation. |
+ """ |
self._members_emitter.Emit('\n /** @domName $DOMINTERFACE.$DOMNAME */', |
DOMINTERFACE=operation.overloads[0].doc_js_interface_name, |
DOMNAME=operation.name) |
+ def EmitEventGetter(self, events_class_name): |
+ self._members_emitter.Emit( |
+ '\n /**' |
+ '\n * @domName EventTarget.addEventListener, ' |
+ 'EventTarget.removeEventListener, EventTarget.dispatchEvent' |
+ '\n */' |
+ '\n $TYPE get on =>\n new $TYPE(this);\n', |
+ TYPE=events_class_name) |
+ |
+ def AddMembers(self, interface, declare_only=False): |
+ for const in sorted(interface.constants, ConstantOutputOrder): |
+ self.AddConstant(const) |
+ |
+ for attr in sorted(interface.attributes, ConstantOutputOrder): |
+ if attr.type.id != 'EventListener': |
+ self.AddAttribute(attr, declare_only) |
+ |
+ # The implementation should define an indexer if the interface directly |
+ # extends List. |
+ element_type = None |
+ requires_indexer = False |
+ if self._interface_type_info.list_item_type(): |
+ self.AddIndexer(self._interface_type_info.list_item_type()) |
+ else: |
+ for parent in self._database.Hierarchy(self._interface): |
+ if parent == self._interface: |
+ continue |
+ parent_type_info = self._type_registry.TypeInfo(parent.id) |
+ if parent_type_info.list_item_type(): |
+ self.AmendIndexer(parent_type_info.list_item_type()) |
+ break |
+ |
+ # Group overloaded operations by id. |
+ operationsById = {} |
+ for operation in interface.operations: |
+ if operation.id not in operationsById: |
+ operationsById[operation.id] = [] |
+ operationsById[operation.id].append(operation) |
+ |
+ # Generate operations. |
+ for id in sorted(operationsById.keys()): |
+ operations = operationsById[id] |
+ info = AnalyzeOperation(interface, operations) |
+ self.AddOperation(info, declare_only) |
+ |
+ def AddSecondaryMembers(self, interface): |
+ # With multiple inheritance, attributes and operations of non-first |
+ # interfaces need to be added. Sometimes the attribute or operation is |
+ # defined in the current interface as well as a parent. In that case we |
+ # avoid making a duplicate definition and pray that the signatures match. |
+ secondary_parents = self._TransitiveSecondaryParents(interface) |
+ for parent_interface in sorted(secondary_parents): |
+ if isinstance(parent_interface, str): |
+ continue |
+ for attr in sorted(parent_interface.attributes, ConstantOutputOrder): |
+ if not FindMatchingAttribute(interface, attr): |
+ self.SecondaryContext(parent_interface) |
+ self.AddAttribute(attr) |
+ |
+ # Group overloaded operations by id. |
+ operationsById = {} |
+ for operation in parent_interface.operations: |
+ if operation.id not in operationsById: |
+ operationsById[operation.id] = [] |
+ operationsById[operation.id].append(operation) |
+ |
+ # Generate operations. |
+ for id in sorted(operationsById.keys()): |
+ if not any(op.id == id for op in interface.operations): |
+ operations = operationsById[id] |
+ info = AnalyzeOperation(interface, operations) |
+ self.SecondaryContext(parent_interface) |
+ self.AddOperation(info) |
+ |
+ def AddAttribute(self, attribute, declare_only=False): |
+ """ Adds an attribute to the generated class. |
+ Arguments: |
+ attribute - The attribute which is to be added. |
+ declare_only- True if the attribute should be declared as an abstract |
+ member and not include invocation code. |
+ """ |
+ dom_name = DartDomNameOfAttribute(attribute) |
+ attr_name = self._renamer.RenameMember( |
+ self._interface.id, attribute, dom_name, 'get:') |
+ if not attr_name or self._IsPrivate(attr_name): |
+ return |
+ |
+ html_setter_name = self._renamer.RenameMember( |
+ self._interface.id, attribute, dom_name, 'set:') |
+ read_only = (attribute.is_read_only or 'Replaceable' in attribute.ext_attrs |
+ or not html_setter_name) |
+ |
+ # We don't yet handle inconsistent renames of the getter and setter yet. |
+ assert(not html_setter_name or attr_name == html_setter_name) |
+ |
+ if declare_only: |
+ self.DeclareAttribute(attribute, |
+ self.SecureOutputType(attribute.type.id), attr_name, read_only) |
+ else: |
+ self.EmitAttribute(attribute, attr_name, read_only) |
+ |
+ def AddOperation(self, info, declare_only=False): |
+ """ Adds an operation to the generated class. |
+ Arguments: |
+ info - The operation info of the operation to be added. |
+ declare_only- True if the operation should be declared as an abstract |
+ member and not include invocation code. |
+ """ |
+ # FIXME: When we pass in operations[0] below, we're assuming all |
+ # overloaded operations have the same security attributes. This |
+ # is currently true, but we should consider filtering earlier or |
+ # merging the relevant data into info itself. |
+ method_name = self._renamer.RenameMember(self._interface.id, |
+ info.operations[0], |
+ info.name) |
+ if not method_name: |
+ if info.name == 'item': |
+ # FIXME: item should be renamed to operator[], not removed. |
+ self.EmitOperation(info, '_item') |
+ return |
+ |
+ if declare_only: |
+ self.DeclareOperation(info, |
+ self.SecureOutputType(info.type_name), method_name) |
+ else: |
+ self.EmitOperation(info, method_name) |
+ |
def AdditionalImplementedInterfaces(self): |
# TODO: Include all implemented interfaces, including other Lists. |
implements = [] |
@@ -37,6 +184,13 @@ class HtmlDartGenerator(object): |
def AddConstructors(self, constructors, factory_provider, class_name, |
base_class): |
+ """ Adds all of the constructors. |
+ Arguments: |
+ constructors - List of the constructors to be added. |
+ factory_provider - Name of the factory provider for this class. |
+ class_name - The name of this class. |
+ base_class - The name of the base class which this extends. |
+ """ |
for constructor_info in constructors: |
self._AddConstructor(constructor_info, factory_provider) |
@@ -55,7 +209,8 @@ class HtmlDartGenerator(object): |
' factory $CTOR.fromList(List<$TYPE> list) =>\n' |
' $FACTORY.create$(CTOR)_fromList(list);\n' |
'\n' |
- ' factory $CTOR.fromBuffer(ArrayBuffer buffer, [int byteOffset, int length]) => \n' |
+ ' factory $CTOR.fromBuffer(ArrayBuffer buffer, ' |
+ '[int byteOffset, int length]) => \n' |
' $FACTORY.create$(CTOR)_fromBuffer(buffer, byteOffset, length);\n', |
CTOR=self._interface.id, |
TYPE=self._DartType(typed_array_type), |
@@ -65,8 +220,9 @@ class HtmlDartGenerator(object): |
constructor_info.GenerateFactoryInvocation( |
self._DartType, self._members_emitter, factory_provider) |
- def DeclareAttribute(self, attribute, type_name, html_name, read_only): |
- # Declares an attribute but does not include the code to invoke it. |
+ def DeclareAttribute(self, attribute, type_name, attr_name, read_only): |
+ """ Declares an attribute but does not include the code to invoke it. |
+ """ |
self.EmitAttributeDocumentation(attribute) |
if read_only: |
template = '\n $TYPE get $NAME;\n' |
@@ -74,15 +230,74 @@ class HtmlDartGenerator(object): |
template = '\n $TYPE $NAME;\n' |
self._members_emitter.Emit(template, |
- NAME=html_name, |
+ NAME=attr_name, |
TYPE=type_name) |
- def DeclareOperation(self, operation, type_name, html_name): |
- # Declares an operation but does not include the code to invoke it. |
+ def DeclareOperation(self, operation, return_type_name, method_name): |
+ """ Declares an operation but does not include the code to invoke it. |
+ Arguments: |
+ operation - The operation to be declared. |
+ return_type_name - The name of the return type. |
+ method_name - The name of the method. |
+ """ |
self.EmitOperationDocumentation(operation) |
self._members_emitter.Emit( |
'\n' |
' $TYPE $NAME($PARAMS);\n', |
- TYPE=type_name, |
- NAME=html_name, |
+ TYPE=return_type_name, |
+ NAME=method_name, |
PARAMS=operation.ParametersDeclaration(self._DartType)) |
+ |
+ def SecureOutputType(self, type_name, is_dart_type=False): |
+ """ Converts the type name to the secure type name for return types. |
+ """ |
+ if is_dart_type: |
+ dart_name = type_name |
+ else: |
+ dart_name = self._DartType(type_name) |
+ # We only need to secure Window. Only local History and Location are |
+ # returned in generated code. |
+ assert(dart_name != 'History' and dart_name != 'Location') |
+ if dart_name == 'LocalWindow': |
+ return _secure_base_types[dart_name] |
+ return dart_name |
+ |
+ def SecureBaseName(self, type_name): |
+ if type_name in _secure_base_types: |
+ return _secure_base_types[type_name] |
+ |
+ def _TransitiveSecondaryParents(self, interface): |
+ """Returns a list of all non-primary parents. |
+ |
+ The list contains the interface objects for interfaces defined in the |
+ database, and the name for undefined interfaces. |
+ """ |
+ def walk(parents): |
+ for parent in parents: |
+ parent_name = parent.type.id |
+ if parent_name == 'EventTarget': |
+ # Currently EventTarget is implemented as a mixin, not a proper |
+ # super interface---ignore its members. |
+ continue |
+ if IsDartCollectionType(parent_name): |
+ result.append(parent_name) |
+ continue |
+ if self._database.HasInterface(parent_name): |
+ parent_interface = self._database.GetInterface(parent_name) |
+ result.append(parent_interface) |
+ walk(parent_interface.parents) |
+ |
+ result = [] |
+ if interface.parents: |
+ parent = interface.parents[0] |
+ if IsPureInterface(parent.type.id): |
+ walk(interface.parents) |
+ else: |
+ walk(interface.parents[1:]) |
+ return result |
+ |
+ def _DartType(self, type_name): |
+ return self._type_registry.DartType(type_name) |
+ |
+ def _IsPrivate(self, name): |
+ return name.startswith('_') |