b001a58bca
They introduce unnecessary level of indirection with no added value. R=antonm@google.com,vsm@google.com Review URL: https://codereview.chromium.org//10986048 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@12946 260f80e4-7a28-3924-810f-c04153c831b5
390 lines
14 KiB
Python
390 lines
14 KiB
Python
#!/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
|
|
dart2js binding from the IDL database."""
|
|
|
|
import os
|
|
from generator import *
|
|
from systembase import *
|
|
|
|
# Members (getters, setters, and methods) to suppress. These are
|
|
# either removed or custom implemented.
|
|
_dom_dart2js_omitted_members = set([
|
|
# Replace with custom.
|
|
'DOMWindow.get:top',
|
|
'HTMLIFrameElement.get:contentWindow',
|
|
|
|
# Remove.
|
|
'DOMWindow.get:frameElement',
|
|
'HTMLIFrameElement.get:contentDocument',
|
|
])
|
|
|
|
class Dart2JSSystem(System):
|
|
|
|
def __init__(self, options):
|
|
super(Dart2JSSystem, self).__init__(options)
|
|
self._impl_file_paths = []
|
|
|
|
def ProcessInterface(self, interface):
|
|
"""."""
|
|
if IsPureInterface(interface.id):
|
|
return
|
|
template_file = 'impl_%s.darttemplate' % interface.id
|
|
template = self._templates.TryLoad(template_file)
|
|
if not template:
|
|
template = self._templates.Load('dart2js_impl.darttemplate')
|
|
|
|
dart_code = self._ImplFileEmitter(interface.id)
|
|
Dart2JSInterfaceGenerator(self, interface, template, dart_code).Generate()
|
|
|
|
def GenerateLibraries(self):
|
|
self._GenerateLibFile(
|
|
'dart2js_dom.darttemplate',
|
|
os.path.join(self._output_dir, 'dom_dart2js.dart'),
|
|
(self._interface_system._dart_interface_file_paths +
|
|
self._impl_file_paths))
|
|
|
|
def Finish(self):
|
|
pass
|
|
|
|
def _ImplFileEmitter(self, name):
|
|
"""Returns the file emitter of the Dart2JS implementation file."""
|
|
path = os.path.join(self._output_dir, 'src', 'dart2js', '%s.dart' % name)
|
|
self._impl_file_paths.append(path)
|
|
return self._emitters.FileEmitter(path)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
class Dart2JSInterfaceGenerator(BaseGenerator):
|
|
"""Generates a Dart2JS class for a DOM IDL interface."""
|
|
|
|
def __init__(self, options, interface, template, dart_code):
|
|
"""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.
|
|
template: A string template.
|
|
dart_code: an Emitter for the file containing the Dart implementation
|
|
class.
|
|
"""
|
|
super(Dart2JSInterfaceGenerator, self).__init__(
|
|
options.database, options.type_registry, interface)
|
|
self._template_loader = options.templates
|
|
self._interface = interface
|
|
self._template = template
|
|
self._dart_code = dart_code
|
|
self._current_secondary_parent = None
|
|
|
|
def StartInterface(self):
|
|
interface = self._interface
|
|
interface_name = interface.id
|
|
self._class_name = self._ImplClassName(interface_name)
|
|
|
|
base = None
|
|
if interface.parents:
|
|
supertype = interface.parents[0].type.id
|
|
if IsDartCollectionType(supertype):
|
|
# List methods are injected in AddIndexer.
|
|
pass
|
|
elif IsPureInterface(supertype):
|
|
pass
|
|
else:
|
|
base = self._ImplClassName(supertype)
|
|
|
|
native_spec = MakeNativeSpec(interface.javascript_binding_name)
|
|
|
|
if base:
|
|
extends = ' extends ' + base
|
|
elif native_spec[0] == '=':
|
|
# The implementation is a singleton with no prototype.
|
|
extends = ''
|
|
else:
|
|
extends = ' extends _DOMTypeJs'
|
|
|
|
# TODO: Include all implemented interfaces, including other Lists.
|
|
implements = [interface_name]
|
|
element_type = MaybeTypedArrayElementType(self._interface)
|
|
if element_type:
|
|
implements.append('List<%s>' % self._DartType(element_type))
|
|
|
|
self._members_emitter = self._dart_code.Emit(
|
|
self._template,
|
|
#class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
|
|
#$!MEMBERS
|
|
#}
|
|
CLASSNAME=self._class_name,
|
|
EXTENDS=extends,
|
|
IMPLEMENTS=' implements ' + ', '.join(implements),
|
|
NATIVESPEC=' native "' + native_spec + '"')
|
|
|
|
# Emit a factory provider class for the constructor.
|
|
constructor_info = AnalyzeConstructor(interface)
|
|
if constructor_info:
|
|
self._EmitFactoryProvider(interface_name, constructor_info)
|
|
|
|
|
|
def FinishInterface(self):
|
|
"""."""
|
|
pass
|
|
|
|
def _ImplClassName(self, type_name):
|
|
name = type_name
|
|
if type_name in nativified_classes:
|
|
name = nativified_classes[type_name]
|
|
return '_' + type_name + 'Js'
|
|
|
|
def _EmitFactoryProvider(self, interface_name, constructor_info):
|
|
template_file = 'factoryprovider_%s.darttemplate' % interface_name
|
|
template = self._template_loader.TryLoad(template_file)
|
|
if not template:
|
|
template = self._template_loader.Load('factoryprovider.darttemplate')
|
|
|
|
factory_provider = '_' + interface_name + 'FactoryProvider'
|
|
emitter = self._system._ImplFileEmitter(factory_provider)
|
|
emitter.Emit(
|
|
template,
|
|
FACTORYPROVIDER=factory_provider,
|
|
CONSTRUCTOR=interface_name,
|
|
PARAMETERS=constructor_info.ParametersImplementationDeclaration(self._DartType),
|
|
NAMEDCONSTRUCTOR=constructor_info.name or interface_name,
|
|
ARGUMENTS=constructor_info.ParametersAsArgumentList())
|
|
|
|
def _ShouldNarrowToImplementationType(self, type_name):
|
|
# TODO(sra): Move into the 'system' and cache the result.
|
|
do_not_narrow = ['DOMStringList', 'DOMStringMap', 'EventListener',
|
|
'IDBAny', 'IDBKey', 'MediaQueryListListener']
|
|
if type_name in do_not_narrow:
|
|
return False
|
|
if self._database.HasInterface(type_name):
|
|
interface = self._database.GetInterface(type_name)
|
|
# Callbacks are typedef functions so don't have a class.
|
|
return 'Callback' not in interface.ext_attrs
|
|
return False
|
|
|
|
def _NarrowToImplementationType(self, type_name):
|
|
if self._ShouldNarrowToImplementationType(type_name):
|
|
return self._ImplClassName(self._DartType(type_name))
|
|
return self._DartType(type_name)
|
|
|
|
def _NarrowInputType(self, type_name):
|
|
return self._NarrowToImplementationType(type_name)
|
|
|
|
def _NarrowOutputType(self, type_name):
|
|
return self._NarrowToImplementationType(type_name)
|
|
|
|
def AddConstant(self, constant):
|
|
# Since we are currently generating native classes without interfaces,
|
|
# generate the constants as part of the class. This will need to go away
|
|
# if we revert back to generating interfaces.
|
|
self._members_emitter.Emit('\n static const $TYPE $NAME = $VALUE;\n',
|
|
NAME=constant.id,
|
|
TYPE=self._DartType(constant.type.id),
|
|
VALUE=constant.value)
|
|
|
|
pass
|
|
|
|
def OverrideMember(self, member):
|
|
return self._interface.id + '.' + member in _dom_dart2js_omitted_members
|
|
|
|
def AddAttribute(self, attribute):
|
|
getter = attribute
|
|
setter = attribute if not IsReadOnly(attribute) else None
|
|
if getter and self.OverrideMember('get:' + getter.id):
|
|
getter = None
|
|
if setter and self.OverrideMember('set:' + setter.id):
|
|
setter = None
|
|
if not getter and not setter:
|
|
return
|
|
|
|
output_type = getter and self._NarrowOutputType(getter.type.id)
|
|
input_type = setter and self._NarrowInputType(setter.type.id)
|
|
|
|
# If the (getter, setter) pair is shadowing, we can't generate a shadowing
|
|
# field (Issue 1633).
|
|
(super_getter, super_getter_interface) = self._FindShadowedAttribute(getter)
|
|
(super_setter, super_setter_interface) = self._FindShadowedAttribute(setter)
|
|
if super_getter or super_setter:
|
|
if getter and not setter and super_getter and not super_setter:
|
|
if self._DartType(getter.type.id) == self._DartType(super_getter.type.id):
|
|
# Compatible getter, use the superclass property. This works because
|
|
# JavaScript will do its own dynamic dispatch.
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' // Use implementation from $SUPER.\n'
|
|
' // final $TYPE $NAME;\n',
|
|
SUPER=super_getter_interface,
|
|
NAME=DartDomNameOfAttribute(getter),
|
|
TYPE=output_type)
|
|
return
|
|
|
|
self._members_emitter.Emit('\n // Shadowing definition.')
|
|
self._AddAttributeUsingProperties(getter, setter)
|
|
return
|
|
|
|
# Can't generate field if attribute has different name in JS and Dart.
|
|
if self._AttributeChangesName(getter or setter):
|
|
self._AddAttributeUsingProperties(getter, setter)
|
|
return
|
|
|
|
if getter and setter and input_type == output_type:
|
|
self._members_emitter.Emit(
|
|
'\n $TYPE $NAME;\n',
|
|
NAME=DartDomNameOfAttribute(getter),
|
|
TYPE=TypeOrVar(output_type))
|
|
return
|
|
if getter and not setter:
|
|
self._members_emitter.Emit(
|
|
'\n final $OPT_TYPE$NAME;\n',
|
|
NAME=DartDomNameOfAttribute(getter),
|
|
OPT_TYPE=TypeOrNothing(output_type))
|
|
return
|
|
self._AddAttributeUsingProperties(getter, setter)
|
|
|
|
def _AttributeChangesName(self, attr):
|
|
return attr.id != DartDomNameOfAttribute(attr)
|
|
|
|
def _AddAttributeUsingProperties(self, getter, setter):
|
|
if getter:
|
|
self._AddGetter(getter)
|
|
if setter:
|
|
self._AddSetter(setter)
|
|
|
|
def _AddGetter(self, attr):
|
|
# TODO(sra): Remove native body when Issue 829 fixed.
|
|
self._members_emitter.Emit(
|
|
'\n $(OPT_TYPE)get $NAME native "return this.$NATIVE_NAME;";\n',
|
|
NAME=DartDomNameOfAttribute(attr),
|
|
NATIVE_NAME=attr.id,
|
|
OPT_TYPE=TypeOrNothing(self._NarrowOutputType(attr.type.id)))
|
|
|
|
def _AddSetter(self, attr):
|
|
# TODO(sra): Remove native body when Issue 829 fixed.
|
|
self._members_emitter.Emit(
|
|
' void set $NAME($(OPT_TYPE)value)'
|
|
' native "this.$NATIVE_NAME = value;";\n',
|
|
NAME=DartDomNameOfAttribute(attr),
|
|
NATIVE_NAME=attr.id,
|
|
OPT_TYPE=TypeOrNothing(self._NarrowInputType(attr.type.id)))
|
|
|
|
def _FindShadowedAttribute(self, attr, merged_interfaces={}):
|
|
"""Returns (attribute, superinterface) or (None, None)."""
|
|
def FindInParent(interface):
|
|
"""Returns matching attribute in parent, or None."""
|
|
if interface.parents:
|
|
parent = interface.parents[0]
|
|
if IsDartCollectionType(parent.type.id):
|
|
return (None, None)
|
|
if IsPureInterface(parent.type.id):
|
|
return (None, None)
|
|
if self._database.HasInterface(parent.type.id):
|
|
interfaces_to_search_in = []
|
|
if parent.type.id in merged_interfaces:
|
|
# IDL parent was merged into another interface, which became a
|
|
# parent interface in Dart.
|
|
interfaces_to_search_in.append(parent.type.id)
|
|
parent_interface_name = merged_interfaces[parent.type.id]
|
|
else:
|
|
parent_interface_name = parent.type.id
|
|
|
|
for interface_name in merged_interfaces:
|
|
if merged_interfaces[interface_name] == parent_interface_name:
|
|
# IDL parent has another interface that was merged into it.
|
|
interfaces_to_search_in.append(interface_name)
|
|
|
|
interfaces_to_search_in.append(parent_interface_name)
|
|
for interface_name in interfaces_to_search_in:
|
|
interface = self._database.GetInterface(interface_name)
|
|
attr2 = FindMatchingAttribute(interface, attr)
|
|
if attr2:
|
|
return (attr2, parent_interface_name)
|
|
|
|
return FindInParent(
|
|
self._database.GetInterface(parent_interface_name))
|
|
return (None, None)
|
|
|
|
return FindInParent(self._interface) if attr else (None, None)
|
|
|
|
|
|
def AddSecondaryAttribute(self, interface, attribute):
|
|
self.SecondaryContext(interface)
|
|
self.AddAttribute(attribute)
|
|
|
|
def AddSecondaryOperation(self, interface, info):
|
|
self.SecondaryContext(interface)
|
|
self.AddOperation(info)
|
|
|
|
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
|
|
# 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; }
|
|
#
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' $TYPE operator[](int index) native "return this[index];";\n',
|
|
TYPE=self._NarrowOutputType(element_type))
|
|
|
|
if 'CustomIndexedSetter' in self._interface.ext_attrs:
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' void operator[]=(int index, $TYPE value) native "this[index] = value";\n',
|
|
TYPE=self._NarrowInputType(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=self._NarrowInputType(element_type))
|
|
|
|
# TODO(sra): Use separate mixins for mutable implementations of List<T>.
|
|
# TODO(sra): Use separate mixins for typed array implementations of List<T>.
|
|
template_file = 'immutable_list_mixin.darttemplate'
|
|
template = self._template_loader.Load(template_file)
|
|
self._members_emitter.Emit(template, E=self._DartType(element_type))
|
|
|
|
def AddOperation(self, info):
|
|
"""
|
|
Arguments:
|
|
info: An OperationInfo object.
|
|
"""
|
|
# TODO(vsm): Handle overloads.
|
|
params = info.ParametersImplementationDeclaration(
|
|
lambda type_name: self._NarrowInputType(type_name))
|
|
|
|
native_string = ''
|
|
if info.declared_name != info.name:
|
|
native_string = " '%s'" % info.declared_name
|
|
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' $TYPE $NAME($PARAMS) native$NATIVESTRING;\n',
|
|
TYPE=self._NarrowOutputType(info.type_name),
|
|
NAME=info.name,
|
|
PARAMS=params,
|
|
NATIVESTRING=native_string)
|