 Chromium Code Reviews
 Chromium Code Reviews Issue 9290020:
  Add support for native bindings generation to dartgenerator.py.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 9290020:
  Add support for native bindings generation to dartgenerator.py.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| Index: client/dom/scripts/dartgenerator.py | 
| diff --git a/client/dom/scripts/dartgenerator.py b/client/dom/scripts/dartgenerator.py | 
| index 0a8184d4cacfab2ba85606a4f436a565665394fd..5952bbffd3228b2606f6f8fb19932705491772fb 100755 | 
| --- a/client/dom/scripts/dartgenerator.py | 
| +++ b/client/dom/scripts/dartgenerator.py | 
| @@ -365,7 +365,7 @@ class DartGenerator(object): | 
| def Generate(self, database, output_dir, | 
| module_source_preference=[], source_filter=None, | 
| super_database=None, common_prefix=None, super_map={}, | 
| - lib_dir = None): | 
| + lib_dir=None, systems=[]): | 
| """Generates Dart and JS files for the loaded interfaces. | 
| Args: | 
| @@ -390,28 +390,39 @@ class DartGenerator(object): | 
| self._ComputeInheritanceClosure() | 
| + self._systems = [] | 
| + | 
| interface_system = InterfacesSystem( | 
| TemplateLoader(self._template_dir, ['dom/interface', 'dom', '']), | 
| self._database, self._emitters, self._output_dir) | 
| + self._systems.append(interface_system) | 
| - wrapping_system = WrappingImplementationSystem( | 
| - TemplateLoader(self._template_dir, ['dom/wrapping', 'dom', '']), | 
| - self._database, self._emitters, self._output_dir) | 
| + if 'native' in systems: | 
| 
antonm
2012/02/01 13:03:43
nit: some concerns: systems may have system we don
 | 
| + native_system = NativeImplementationSystem( | 
| + TemplateLoader(self._template_dir, ['dom/native', 'dom', '']), | 
| + self._database, self._emitters, self._auxiliary_dir, | 
| + self._output_dir) | 
| - # Makes interface files available for listing in the library for the | 
| - # wrapping implementation. | 
| + self._systems.append(native_system) | 
| - wrapping_system._interface_system = interface_system | 
| + if 'wrapping' in systems: | 
| + wrapping_system = WrappingImplementationSystem( | 
| + TemplateLoader(self._template_dir, ['dom/wrapping', 'dom', '']), | 
| + self._database, self._emitters, self._output_dir) | 
| - frog_system = FrogSystem( | 
| - TemplateLoader(self._template_dir, ['dom/frog', 'dom', '']), | 
| - self._database, self._emitters, self._output_dir) | 
| + # Makes interface files available for listing in the library for the | 
| + # wrapping implementation. | 
| + wrapping_system._interface_system = interface_system | 
| + self._systems.append(wrapping_system) | 
| + | 
| + if 'frog' in systems: | 
| + frog_system = FrogSystem( | 
| + TemplateLoader(self._template_dir, ['dom/frog', 'dom', '']), | 
| + self._database, self._emitters, self._output_dir) | 
| - frog_system._interface_system = interface_system | 
| + frog_system._interface_system = interface_system | 
| + self._systems.append(frog_system) | 
| - self._systems = [interface_system, | 
| - wrapping_system, | 
| - frog_system] | 
| # Render all interfaces into Dart and save them in files. | 
| for interface in database.GetInterfaces(): | 
| @@ -1305,29 +1316,7 @@ class WrappingInterfaceGenerator(object): | 
| self._type_map.Emit(' "$INTERFACE": native_$(CLASS)_create_$(CLASS),\n', | 
| INTERFACE=interface_name, CLASS=self._class_name) | 
| - base = 'DOMWrapperBase' | 
| - if interface.parents: | 
| - 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. | 
| - if (not _IsDartListType(supertype) and | 
| - not supertype == 'EventTarget'): | 
| - base = self._ImplClassName(supertype) | 
| - if _IsDartCollectionType(supertype): | 
| - # List methods are injected in AddIndexer. | 
| - pass | 
| - elif 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']: | 
| - base = self._ImplClassName(supertype) | 
| - else: | 
| - base = self._ImplClassName(supertype) | 
| + base = self._BaseClassName(interface) | 
| (self._members_emitter, | 
| self._top_level_emitter) = self._dart_code.Emit( | 
| @@ -1348,6 +1337,32 @@ class WrappingInterfaceGenerator(object): | 
| 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 | 
| @@ -1441,24 +1456,8 @@ class WrappingInterfaceGenerator(object): | 
| # | 
| # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } | 
| # | 
| - if ('HasIndexGetter' in self._interface.ext_attrs or | 
| - 'HasNumericIndexGetter' in self._interface.ext_attrs): | 
| - 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) | 
| - self._js_code.Emit( | 
| - '\n' | 
| - 'function native_$(CLASS)_$(METHOD)(_this, index) {\n' | 
| - ' try {\n' | 
| - ' return __dom_wrap(_this.$dom[index]);\n' | 
| - ' } catch (e) {\n' | 
| - ' throw __dom_wrap_exception(e);\n' | 
| - ' }\n' | 
| - '}\n', | 
| - CLASS=self._class_name, METHOD=method_name) | 
| + if self._HasNativeIndexGetter(self._interface): | 
| + self._EmitNativeIndexGetter(self._interface, element_type) | 
| else: | 
| self._members_emitter.Emit( | 
| '\n' | 
| @@ -1467,26 +1466,8 @@ class WrappingInterfaceGenerator(object): | 
| ' }\n', | 
| TYPE=element_type) | 
| - | 
| - if 'HasCustomIndexSetter' in self._interface.ext_attrs: | 
| - 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) | 
| - self._js_code.Emit( | 
| - '\n' | 
| - 'function native_$(CLASS)_$(METHOD)(_this, index, value) {\n' | 
| - ' try {\n' | 
| - ' return _this.$dom[index] = __dom_unwrap(value);\n' | 
| - ' } catch (e) {\n' | 
| - ' throw __dom_wrap_exception(e);\n' | 
| - ' }\n' | 
| - '}\n', | 
| - CLASS=self._class_name, METHOD=method_name) | 
| + if self._HasNativeIndexSetter(self._interface): | 
| + self._EmitNativeIndexSetter(self._interface, element_type) | 
| else: | 
| self._members_emitter.Emit( | 
| '\n' | 
| @@ -1584,6 +1565,51 @@ class WrappingInterfaceGenerator(object): | 
| ' }\n', | 
| TYPE=element_type) | 
| + def _HasNativeIndexGetter(self, interface): | 
| + return ('HasIndexGetter' in interface.ext_attrs or | 
| + 'HasNumericIndexGetter' 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) | 
| + self._js_code.Emit( | 
| + '\n' | 
| + 'function native_$(CLASS)_$(METHOD)(_this, index) {\n' | 
| + ' try {\n' | 
| + ' return __dom_wrap(_this.$dom[index]);\n' | 
| + ' } catch (e) {\n' | 
| + ' throw __dom_wrap_exception(e);\n' | 
| + ' }\n' | 
| + '}\n', | 
| + CLASS=self._class_name, METHOD=method_name) | 
| + | 
| + def _HasNativeIndexSetter(self, interface): | 
| + return 'HasCustomIndexSetter' 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) | 
| + self._js_code.Emit( | 
| + '\n' | 
| + 'function native_$(CLASS)_$(METHOD)(_this, index, value) {\n' | 
| + ' try {\n' | 
| + ' return _this.$dom[index] = __dom_unwrap(value);\n' | 
| + ' } catch (e) {\n' | 
| + ' throw __dom_wrap_exception(e);\n' | 
| + ' }\n' | 
| + '}\n', | 
| + CLASS=self._class_name, METHOD=method_name) | 
| + | 
| def AddOperation(self, info): | 
| """ | 
| Arguments: | 
| @@ -2034,3 +2060,223 @@ class FrogInterfaceGenerator(object): | 
| NAME=info.name, | 
| PARAMS=info.ParametersImplementationDeclaration( | 
| lambda type_name: self._NarrowInputType(type_name))) | 
| + | 
| + | 
| +# ------------------------------------------------------------------------------ | 
| + | 
| +class NativeImplementationSystem(System): | 
| + | 
| + def __init__(self, templates, database, emitters, auxiliary_dir, output_dir): | 
| + super(NativeImplementationSystem, self).__init__( | 
| + templates, database, emitters, output_dir) | 
| + | 
| + self._auxiliary_dir = auxiliary_dir | 
| + self._dom_public_files = [] | 
| + self._dom_impl_files = [] | 
| + | 
| + def InterfaceGenerator(self, | 
| + interface, | 
| + common_prefix, | 
| + super_interface_name, | 
| + source_filter): | 
| + interface_name = interface.id | 
| + | 
| + dart_interface_path = self._FilePathForDartInterface(interface_name) | 
| + self._dom_public_files.append(dart_interface_path) | 
| + | 
| + dart_impl_path = self._FilePathForDartImplementation(interface_name) | 
| + self._dom_impl_files.append(dart_impl_path) | 
| + | 
| + return NativeImplementationGenerator(interface, super_interface_name, | 
| + self._emitters.FileEmitter(dart_impl_path), | 
| + self._BaseDefines(interface), | 
| + self._templates) | 
| + | 
| + def ProcessCallback(self, interface, info): | 
| + self._dom_public_files.append(self._FilePathForDartInterface(interface.id)) | 
| + | 
| + def GenerateLibraries(self, lib_dir): | 
| + auxiliary_dir = os.path.relpath(self._auxiliary_dir, self._output_dir) | 
| + | 
| + # Generate dom_public.dart. | 
| + dom_public_path = os.path.join(self._output_dir, 'dom_public.dart') | 
| + | 
| + dom_public_imports_emitter = emitter.Emitter() | 
| + for file in self._dom_public_files: | 
| + path = os.path.relpath(file, os.path.dirname(dom_public_path)) | 
| + dom_public_imports_emitter.Emit('#source("$PATH");\n', PATH=path) | 
| + | 
| + dom_public_emitter = self._emitters.FileEmitter(dom_public_path) | 
| + dom_public_emitter.Emit(self._templates.Load('dom_public.darttemplate'), | 
| + AUXILIARY_DIR=auxiliary_dir, | 
| + SOURCES=dom_public_imports_emitter.Fragments()) | 
| + | 
| + # Generate dom_impl.dart. | 
| + dom_impl_path = os.path.join(self._output_dir, 'dom_impl.dart') | 
| + | 
| + dom_impl_imports_emitter = emitter.Emitter() | 
| + for file in self._dom_impl_files: | 
| + path = os.path.relpath(file, os.path.dirname(dom_impl_path)) | 
| + dom_impl_imports_emitter.Emit('#source("$PATH");\n', PATH=path) | 
| + | 
| + dom_impl_emitter = self._emitters.FileEmitter(dom_impl_path) | 
| + dom_impl_emitter.Emit(self._templates.Load('dom_impl.darttemplate'), | 
| + AUXILIARY_DIR=auxiliary_dir, | 
| + SOURCES=dom_impl_imports_emitter.Fragments()) | 
| + | 
| + def Finish(self): | 
| + pass | 
| + | 
| + def _FilePathForDartInterface(self, interface_name): | 
| + return os.path.join(self._output_dir, 'src', 'interface', | 
| + '%s.dart' % interface_name) | 
| + | 
| + def _FilePathForDartImplementation(self, interface_name): | 
| + return os.path.join(self._output_dir, 'dart', | 
| + '%sImplementation.dart' % interface_name) | 
| + | 
| + | 
| +class NativeImplementationGenerator(WrappingInterfaceGenerator): | 
| + """Generates Dart implementation for one DOM IDL interface.""" | 
| + | 
| + def __init__(self, interface, super_interface, dart_impl_emitter, | 
| + base_members, templates): | 
| + """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. | 
| + super_interface: A string or None, the name of the common interface that | 
| + this interface implements, if any. | 
| + dart_impl_emitter: 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_impl_emitter = dart_impl_emitter | 
| + self._base_members = base_members | 
| + self._templates = templates | 
| + self._current_secondary_parent = None | 
| + | 
| + def StartInterface(self): | 
| + self._class_name = self._ImplClassName(self._interface.id) | 
| + self._members_emitter = emitter.Emitter() | 
| + | 
| + def _ImplClassName(self, type_name): | 
| + return type_name + 'Implementation' | 
| + | 
| + def FinishInterface(self): | 
| + interface = self._interface | 
| + interface_name = interface.id | 
| + | 
| + base = self._BaseClassName(interface) | 
| + self._dart_impl_emitter.Emit( | 
| + self._templates.Load('dart_implementation.darttemplate'), | 
| + CLASS=self._class_name, BASE=base, INTERFACE=interface_name, | 
| + MEMBERS=self._members_emitter.Fragments()) | 
| + | 
| + def AddGetter(self, attr): | 
| + self._members_emitter.Emit( | 
| + '\n' | 
| + ' $TYPE get $NAME() native "$(INTERFACE)_$(NAME)_Getter";\n', | 
| + NAME=attr.id, TYPE=attr.type.id, INTERFACE=self._interface.id) | 
| + | 
| + def AddSetter(self, attr): | 
| + self._members_emitter.Emit( | 
| + '\n' | 
| + ' void set $NAME($TYPE) native "$(INTERFACE)_$(NAME)_Setter";\n', | 
| + NAME=attr.id, TYPE=attr.type.id, INTERFACE=self._interface.id) | 
| + | 
| + def _HasNativeIndexGetter(self, interface): | 
| + return ('HasCustomIndexGetter' in interface.ext_attrs or | 
| + 'HasNumericIndexGetter' in interface.ext_attrs) | 
| + | 
| + def _EmitNativeIndexGetter(self, interface, element_type): | 
| + native_binding = '%s_numericIndexGetter_Callback' % interface.id | 
| + self._members_emitter.Emit( | 
| + '\n' | 
| + ' $TYPE operator[](int index) native "$NATIVE_BINDING";\n', | 
| + TYPE=element_type, NATIVE_BINDING=native_binding) | 
| + | 
| + def _EmitNativeIndexSetter(self, interface, element_type): | 
| + native_binding = '%s_numericIndexSetter_Callback' % self._interface.id | 
| + self._members_emitter.Emit( | 
| + '\n' | 
| + ' void operator[]=(int index, $TYPE value) native "$NATIVE_BINDING";\n', | 
| + TYPE=element_type, NATIVE_BINDING=native_binding) | 
| + | 
| + def AddOperation(self, info): | 
| + """ | 
| + Arguments: | 
| + info: An OperationInfo object. | 
| + """ | 
| + | 
| + if 'Custom' in info.overloads[0].ext_attrs: | 
| + self._members_emitter.Emit( | 
| + '\n' | 
| + ' $TYPE $NAME($PARAMETERS) native "$(INTERFACE)_$(NAME)_Callback";\n', | 
| + TYPE=info.type_name, | 
| + NAME=info.name, | 
| + PARAMETERS=info.ParametersImplementationDeclaration(), | 
| + INTERFACE=self._interface.id) | 
| + return | 
| + | 
| + body = self._members_emitter.Emit( | 
| + '\n' | 
| + ' $TYPE $NAME($PARAMETERS) {\n' | 
| + '$!BODY' | 
| + ' }\n', | 
| + TYPE=info.type_name, | 
| + NAME=info.name, | 
| + PARAMETERS=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. | 
| + """ | 
| + | 
| + arg_names = [info.arg_infos[i][0] | 
| + for (i, arg) in enumerate(operation.arguments)] | 
| + | 
| + self._native_version += 1 | 
| + native_name = '_%s' % info.name | 
| + if self._native_version > 1: | 
| + native_name = '%s_%s' % (native_name, self._native_version) | 
| + | 
| + argument_expressions = ', '.join(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(' $TYPE $NATIVE_NAME($PARAMS) native ' | 
| + '"$(INTERFACE)$(NATIVE_NAME)_Callback";\n', | 
| + NATIVE_NAME=native_name, | 
| + TYPE=info.type_name, | 
| + PARAMS=', '.join(arg_names), | 
| + INTERFACE=self._interface.id) |