Merge wrapping system into native system.
R=antonm@chromium.org Review URL: https://chromiumcodereview.appspot.com//10388085 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7546 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -149,7 +149,7 @@ def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('--systems', dest='systems',
|
||||
action='store', type='string',
|
||||
default='frog,dummy,wrapping,htmlfrog,htmldartium',
|
||||
default='frog,dummy,htmlfrog,htmldartium',
|
||||
help='Systems to generate (frog, native, dummy, '
|
||||
'htmlfrog, htmldartium)')
|
||||
parser.add_option('--output-dir', dest='output_dir',
|
||||
|
||||
@@ -18,7 +18,6 @@ from systemfrog import *
|
||||
from systemhtml import *
|
||||
from systeminterface import *
|
||||
from systemnative import *
|
||||
from systemwrapping import *
|
||||
from templateloader import TemplateLoader
|
||||
|
||||
_logger = logging.getLogger('dartgenerator')
|
||||
@@ -258,16 +257,6 @@ class DartGenerator(object):
|
||||
|
||||
self._systems.append(native_system)
|
||||
|
||||
if 'wrapping' in systems:
|
||||
wrapping_system = WrappingImplementationSystem(
|
||||
TemplateLoader(self._template_dir, ['dom/wrapping', '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 'dummy' in systems:
|
||||
dummy_system = DummyImplementationSystem(
|
||||
TemplateLoader(self._template_dir, ['dom/dummy', 'dom', '']),
|
||||
|
||||
@@ -8,7 +8,6 @@ native binding from the IDL database."""
|
||||
|
||||
import emitter
|
||||
import os
|
||||
import systemwrapping
|
||||
from generator import *
|
||||
from systembase import *
|
||||
|
||||
@@ -206,7 +205,7 @@ class NativeImplementationSystem(System):
|
||||
return os.path.join(self._output_dir, 'cpp', 'Dart%s.cpp' % interface_name)
|
||||
|
||||
|
||||
class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
class NativeImplementationGenerator(object):
|
||||
"""Generates Dart implementation for one DOM IDL interface."""
|
||||
|
||||
def __init__(self, system, interface,
|
||||
@@ -318,6 +317,32 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
def _ImplClassName(self, interface_name):
|
||||
return interface_name + 'Implementation'
|
||||
|
||||
def _BaseClassName(self):
|
||||
if not self._interface.parents:
|
||||
return 'DOMWrapperBase'
|
||||
|
||||
supertype = self._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 self._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 self._interface.operations if op.id == 'addEventListener']:
|
||||
return self._ImplClassName(supertype)
|
||||
return 'DOMWrapperBase'
|
||||
|
||||
return self._ImplClassName(supertype)
|
||||
|
||||
def _IsConstructable(self):
|
||||
# FIXME: support ConstructorTemplate.
|
||||
return set(['CustomConstructor', 'V8CustomConstructor', 'Constructor', 'NamedConstructor']) & set(self._interface.ext_attrs)
|
||||
@@ -365,7 +390,7 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
NATIVE_NAME=native_implementation_function)
|
||||
|
||||
def FinishInterface(self):
|
||||
base = self._BaseClassName(self._interface)
|
||||
base = self._BaseClassName()
|
||||
self._dart_impl_emitter.Emit(
|
||||
self._templates.Load('dart_implementation.darttemplate'),
|
||||
CLASS=self._class_name, BASE=base, INTERFACE=self._interface.id,
|
||||
@@ -458,6 +483,10 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
|
||||
return False
|
||||
|
||||
def AddConstant(self, constant):
|
||||
# Constants are already defined on the interface.
|
||||
pass
|
||||
|
||||
def AddAttribute(self, getter, setter):
|
||||
if 'CheckSecurityForNode' in (getter or setter).ext_attrs:
|
||||
# FIXME: exclude from interface as well.
|
||||
@@ -468,6 +497,9 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
if setter:
|
||||
self._AddSetter(setter)
|
||||
|
||||
def AddSecondaryAttribute(self, interface, getter, setter):
|
||||
self.AddAttribute(getter, setter)
|
||||
|
||||
def _AddGetter(self, attr):
|
||||
type_info = GetIDLTypeInfo(attr.type.id)
|
||||
dart_declaration = '%s get %s()' % (
|
||||
@@ -549,16 +581,142 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
self._GenerateNativeCallback(cpp_callback_name, parameter_definitions_emitter.Fragments(),
|
||||
True, invocation, raises_exceptions=True)
|
||||
|
||||
def _HasNativeIndexGetter(self, interface):
|
||||
return ('CustomIndexedGetter' in interface.ext_attrs or
|
||||
'NumericIndexedGetter' in interface.ext_attrs)
|
||||
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; }
|
||||
#
|
||||
dart_element_type = DartType(element_type)
|
||||
if ('CustomIndexedGetter' in self._interface.ext_attrs or
|
||||
'NumericIndexedGetter' in self._interface.ext_attrs):
|
||||
self._EmitNativeIndexGetter(dart_element_type)
|
||||
else:
|
||||
self._members_emitter.Emit(
|
||||
'\n'
|
||||
' $TYPE operator[](int index) {\n'
|
||||
' return item(index);\n'
|
||||
' }\n',
|
||||
TYPE=dart_element_type)
|
||||
|
||||
def _EmitNativeIndexGetter(self, interface, element_type):
|
||||
if 'CustomIndexedSetter' in self._interface.ext_attrs:
|
||||
self._EmitNativeIndexSetter(dart_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=dart_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=dart_element_type)
|
||||
|
||||
def _EmitNativeIndexGetter(self, element_type):
|
||||
dart_declaration = '%s operator[](int index)' % element_type
|
||||
self._GenerateNativeBinding('numericIndexGetter', 2, dart_declaration,
|
||||
'Callback', True)
|
||||
|
||||
def _EmitNativeIndexSetter(self, interface, element_type):
|
||||
def _EmitNativeIndexSetter(self, element_type):
|
||||
dart_declaration = 'void operator[]=(int index, %s value)' % element_type
|
||||
self._GenerateNativeBinding('numericIndexSetter', 3, dart_declaration,
|
||||
'Callback', True)
|
||||
@@ -603,12 +761,133 @@ class NativeImplementationGenerator(systemwrapping.WrappingInterfaceGenerator):
|
||||
if fallthrough:
|
||||
body.Emit(' throw "Incorrect number or type of arguments";\n');
|
||||
|
||||
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)
|
||||
|
||||
def ShouldGenerateSingleOperation():
|
||||
if position == len(info.param_infos):
|
||||
if len(overloads) > 1:
|
||||
raise Exception('Duplicate operations ' + str(overloads))
|
||||
return True
|
||||
|
||||
# Check if we dispatch on RequiredCppParameter arguments. In this
|
||||
# case all trailing arguments must be RequiredCppParameter and there
|
||||
# is no need in dispatch.
|
||||
# TODO(antonm): better diagnositics.
|
||||
if position >= len(overloads[0].arguments):
|
||||
def IsRequiredCppParameter(arg):
|
||||
return 'RequiredCppParameter' in arg.ext_attrs
|
||||
last_overload = overloads[-1]
|
||||
if (len(last_overload.arguments) > position and
|
||||
IsRequiredCppParameter(last_overload.arguments[position])):
|
||||
for overload in overloads:
|
||||
args = overload.arguments[position:]
|
||||
if not all([IsRequiredCppParameter(arg) for arg in args]):
|
||||
raise Exception('Invalid overload for RequiredCppParameter')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if ShouldGenerateSingleOperation():
|
||||
self.GenerateSingleOperation(emitter, info, indent, overloads[-1])
|
||||
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 = info.param_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 = DartType(first_overload.arguments[position].type.id)
|
||||
test = TypeCheck(param.name, type)
|
||||
pred = lambda op: len(op.arguments) > position and DartType(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.dart_type:
|
||||
return self.GenerateDispatch(
|
||||
emitter, info, indent, position + 1, positive)
|
||||
|
||||
# Otherwise the overloads have the same type but the type is a subtype 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
|
||||
|
||||
def AddOperation(self, info):
|
||||
self._AddOperation(info)
|
||||
|
||||
def AddStaticOperation(self, info):
|
||||
self._AddOperation(info)
|
||||
|
||||
def AddSecondaryOperation(self, interface, info):
|
||||
self.AddOperation(info)
|
||||
|
||||
def GenerateSingleOperation(self, dispatch_emitter, info, indent, operation):
|
||||
"""Generates a call to a single operation.
|
||||
|
||||
|
||||
@@ -1,546 +0,0 @@
|
||||
#!/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=DartDomNameOfAttribute(attr),
|
||||
TYPE=DartType(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=DartDomNameOfAttribute(attr),
|
||||
TYPE=DartType(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 _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; }
|
||||
#
|
||||
dart_element_type = DartType(element_type)
|
||||
if self._HasNativeIndexGetter(self._interface):
|
||||
self._EmitNativeIndexGetter(self._interface, dart_element_type)
|
||||
else:
|
||||
self._members_emitter.Emit(
|
||||
'\n'
|
||||
' $TYPE operator[](int index) {\n'
|
||||
' return item(index);\n'
|
||||
' }\n',
|
||||
TYPE=dart_element_type)
|
||||
|
||||
if self._HasNativeIndexSetter(self._interface):
|
||||
self._EmitNativeIndexSetter(self._interface, dart_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=dart_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=dart_element_type)
|
||||
|
||||
def _HasNativeIndexGetter(self, interface):
|
||||
return ('IndexedGetter' in interface.ext_attrs or
|
||||
'NumericIndexedGetter' in interface.ext_attrs)
|
||||
|
||||
def _EmitNativeIndexGetter(self, interface, dart_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=dart_element_type, METHOD=method_name)
|
||||
|
||||
def _HasNativeIndexSetter(self, interface):
|
||||
return 'CustomIndexedSetter' in interface.ext_attrs
|
||||
|
||||
def _EmitNativeIndexSetter(self, interface, dart_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=dart_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 AddStaticOperation(self, info):
|
||||
pass
|
||||
|
||||
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(param_info, overload_arg):
|
||||
return (param_info.name,
|
||||
UnwrapArgExpression(param_info.name, param_info.dart_type))
|
||||
|
||||
names_and_unwrappers = [ArgNameAndUnwrapper(info.param_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)
|
||||
|
||||
def ShouldGenerateSingleOperation():
|
||||
if position == len(info.param_infos):
|
||||
if len(overloads) > 1:
|
||||
raise Exception('Duplicate operations ' + str(overloads))
|
||||
return True
|
||||
|
||||
# Check if we dispatch on RequiredCppParameter arguments. In this
|
||||
# case all trailing arguments must be RequiredCppParameter and there
|
||||
# is no need in dispatch.
|
||||
# TODO(antonm): better diagnositics.
|
||||
if position >= len(overloads[0].arguments):
|
||||
def IsRequiredCppParameter(arg):
|
||||
return 'RequiredCppParameter' in arg.ext_attrs
|
||||
last_overload = overloads[-1]
|
||||
if (len(last_overload.arguments) > position and
|
||||
IsRequiredCppParameter(last_overload.arguments[position])):
|
||||
for overload in overloads:
|
||||
args = overload.arguments[position:]
|
||||
if not all([IsRequiredCppParameter(arg) for arg in args]):
|
||||
raise Exception('Invalid overload for RequiredCppParameter')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if ShouldGenerateSingleOperation():
|
||||
self.GenerateSingleOperation(emitter, info, indent, overloads[-1])
|
||||
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 = info.param_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 = DartType(first_overload.arguments[position].type.id)
|
||||
test = TypeCheck(param.name, type)
|
||||
pred = lambda op: len(op.arguments) > position and DartType(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.dart_type:
|
||||
return self.GenerateDispatch(
|
||||
emitter, info, indent, position + 1, positive)
|
||||
|
||||
# Otherwise the overloads have the same type but the type is a subtype 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
|
||||
Reference in New Issue
Block a user