Files
sdk/lib/dom/scripts/dartgenerator.py
T

643 lines
23 KiB
Python
Executable File

#!/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 generates Dart APIs from the IDL database."""
import emitter
import idlnode
import logging
import multiemitter
import os
import re
import shutil
from generator import *
from systembase import *
from systemfrog import *
from systemhtml import *
from systeminterface import *
from systemnative import *
from systemwrapping import *
from templateloader import TemplateLoader
_logger = logging.getLogger('dartgenerator')
def MergeNodes(node, other):
node.operations.extend(other.operations)
for attribute in other.attributes:
if not node.has_attribute(attribute):
node.attributes.append(attribute)
node.constants.extend(other.constants)
class DartGenerator(object):
"""Utilities to generate Dart APIs and corresponding JavaScript."""
def __init__(self, auxiliary_dir, template_dir, base_package):
"""Constructor for the DartGenerator.
Args:
auxiliary_dir -- location of auxiliary handwritten classes
template_dir -- location of template files
base_package -- the base package name for the generated code.
"""
self._auxiliary_dir = auxiliary_dir
self._template_dir = template_dir
self._base_package = base_package
self._auxiliary_files = {}
self._dart_templates_re = re.compile(r'[\w.:]+<([\w\.<>:]+)>')
self._emitters = None # set later
def _StripModules(self, type_name):
return type_name.split('::')[-1]
def _IsCompoundType(self, database, type_name):
if IsPrimitiveType(type_name):
return True
striped_type_name = self._StripModules(type_name)
if database.HasInterface(striped_type_name):
return True
dart_template_match = self._dart_templates_re.match(type_name)
if dart_template_match:
# Dart templates
parent_type_name = type_name[0 : dart_template_match.start(1) - 1]
sub_type_name = dart_template_match.group(1)
return (self._IsCompoundType(database, parent_type_name) and
self._IsCompoundType(database, sub_type_name))
return False
def _IsDartType(self, type_name):
return '.' in type_name
def LoadAuxiliary(self):
def Visitor(_, dirname, names):
for name in names:
if name.endswith('.dart'):
name = name[0:-5] # strip off ".dart"
self._auxiliary_files[name] = os.path.join(dirname, name)
os.path.walk(self._auxiliary_dir, Visitor, None)
def RenameTypes(self, database, conversion_table, rename_javascript_binding_names):
"""Renames interfaces using the given conversion table.
References through all interfaces will be renamed as well.
Args:
database: the database to apply the renames to.
conversion_table: maps old names to new names.
"""
if conversion_table is None:
conversion_table = {}
# Rename interfaces:
for old_name, new_name in conversion_table.items():
if database.HasInterface(old_name):
_logger.info('renaming interface %s to %s' % (old_name, new_name))
interface = database.GetInterface(old_name)
database.DeleteInterface(old_name)
if not database.HasInterface(new_name):
interface.id = new_name
database.AddInterface(interface)
else:
new_interface = database.GetInterface(new_name)
MergeNodes(new_interface, interface)
if rename_javascript_binding_names:
interface.javascript_binding_name = new_name
interface.doc_js_name = new_name
for member in (interface.operations + interface.constants
+ interface.attributes):
member.doc_js_interface_name = new_name
# Fix references:
for interface in database.GetInterfaces():
for idl_type in interface.all(idlnode.IDLType):
type_name = self._StripModules(idl_type.id)
if type_name in conversion_table:
idl_type.id = conversion_table[type_name]
def FilterMembersWithUnidentifiedTypes(self, database):
"""Removes unidentified types.
Removes constants, attributes, operations and parents with unidentified
types.
"""
for interface in database.GetInterfaces():
def IsIdentified(idl_node):
node_name = idl_node.id if idl_node.id else 'parent'
for idl_type in idl_node.all(idlnode.IDLType):
type_name = idl_type.id
if (type_name is not None and
self._IsCompoundType(database, type_name)):
continue
_logger.warn('removing %s in %s which has unidentified type %s' %
(node_name, interface.id, type_name))
return False
return True
interface.constants = filter(IsIdentified, interface.constants)
interface.attributes = filter(IsIdentified, interface.attributes)
interface.operations = filter(IsIdentified, interface.operations)
interface.parents = filter(IsIdentified, interface.parents)
def FilterInterfaces(self, database,
and_annotations=[],
or_annotations=[],
exclude_displaced=[],
exclude_suppressed=[]):
"""Filters a database to remove interfaces and members that are missing
annotations.
The FremontCut IDLs use annotations to specify implementation
status in various platforms. For example, if a member is annotated
with @WebKit, this means that the member is supported by WebKit.
Args:
database -- the database to filter
all_annotations -- a list of annotation names a member has to
have or it will be filtered.
or_annotations -- if a member has one of these annotations, it
won't be filtered even if it is missing some of the
all_annotations.
exclude_displaced -- if a member has this annotation and it
is marked as displaced it will always be filtered.
exclude_suppressed -- if a member has this annotation and it
is marked as suppressed it will always be filtered.
"""
# Filter interfaces and members whose annotations don't match.
for interface in database.GetInterfaces():
def HasAnnotations(idl_node):
"""Utility for determining if an IDLNode has all
the required annotations"""
for a in exclude_displaced:
if (a in idl_node.annotations
and 'via' in idl_node.annotations[a]):
return False
for a in exclude_suppressed:
if (a in idl_node.annotations
and 'suppressed' in idl_node.annotations[a]):
return False
for a in or_annotations:
if a in idl_node.annotations:
return True
if and_annotations == []:
return False
for a in and_annotations:
if a not in idl_node.annotations:
return False
return True
if HasAnnotations(interface):
interface.constants = filter(HasAnnotations, interface.constants)
interface.attributes = filter(HasAnnotations, interface.attributes)
interface.operations = filter(HasAnnotations, interface.operations)
interface.parents = filter(HasAnnotations, interface.parents)
else:
database.DeleteInterface(interface.id)
self.FilterMembersWithUnidentifiedTypes(database)
def Generate(self, database, output_dir,
module_source_preference=[], source_filter=None,
super_database=None, common_prefix=None, super_map={},
html_map={}, lib_dir=None, systems=[]):
"""Generates Dart and JS files for the loaded interfaces.
Args:
database -- database containing interfaces to generate code for.
output_dir -- directory to write generated files to.
module_source_preference -- priority order list of source annotations to
use when choosing a module name, if none specified uses the module name
from the database.
source_filter -- if specified, only outputs interfaces that have one of
these source annotation and rewrites the names of superclasses not
marked with this source to use the common prefix.
super_database -- database containing super interfaces that the generated
interfaces should extend.
common_prefix -- prefix for the common library, if any.
lib_file_path -- filename for generated .lib file, None if not required.
lib_template -- template file in this directory for generated lib file.
"""
self._emitters = multiemitter.MultiEmitter()
self._database = database
self._output_dir = output_dir
self._FixEventTargets()
self._ComputeInheritanceClosure()
self._systems = []
# TODO(jmesserly): only create these if needed
if ('htmlfrog' in systems) or ('htmldartium' in systems):
html_interface_system = HtmlInterfacesSystem(
TemplateLoader(self._template_dir, ['html/interface', 'html', '']),
self._database, self._emitters, self._output_dir, self)
self._systems.append(html_interface_system)
else:
interface_system = InterfacesSystem(
TemplateLoader(self._template_dir, ['dom/interface', 'dom', '']),
self._database, self._emitters, self._output_dir)
self._systems.append(interface_system)
if 'native' in systems:
native_system = NativeImplementationSystem(
TemplateLoader(self._template_dir, ['dom/native', 'dom', '']),
self._database, self._emitters, self._auxiliary_dir,
self._output_dir)
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', '']),
self._database, self._emitters, self._output_dir)
# Makes interface files available for listing in the library for the
# dummy implementation.
dummy_system._interface_system = interface_system
self._systems.append(dummy_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
self._systems.append(frog_system)
if 'htmlfrog' in systems:
html_system = HtmlFrogSystem(
TemplateLoader(self._template_dir,
['html/frog', 'html/impl', 'html', ''],
{'DARTIUM': False, 'FROG': True}),
self._database, self._emitters, self._output_dir, self)
html_system._interface_system = html_interface_system
self._systems.append(html_system)
if 'htmldartium' in systems:
html_system = HtmlDartiumSystem(
TemplateLoader(self._template_dir,
['html/dartium', 'html/impl', 'html', ''],
{'DARTIUM': True, 'FROG': False}),
self._database, self._emitters, self._auxiliary_dir,
self._output_dir, self)
html_system._interface_system = html_interface_system
self._systems.append(html_system)
# Collect interfaces
interfaces = []
for interface in database.GetInterfaces():
if not MatchSourceFilter(source_filter, interface):
# Skip this interface since it's not present in the required source
_logger.info('Omitting interface - %s' % interface.id)
continue
interfaces.append(interface)
# TODO(sra): Use this list of exception names to generate information to
# tell Frog which exceptions can be passed from JS to Dart code.
exceptions = self._CollectExceptions(interfaces)
# Render all interfaces into Dart and save them in files.
for interface in self._PreOrderInterfaces(interfaces):
super_interface = None
super_name = interface.id
if super_name in super_map:
super_name = super_map[super_name]
if (super_database is not None and
super_database.HasInterface(super_name)):
super_interface = super_name
interface_name = interface.id
auxiliary_file = self._auxiliary_files.get(interface_name)
if auxiliary_file is not None:
_logger.info('Skipping %s because %s exists' % (
interface_name, auxiliary_file))
continue
info = RecognizeCallback(interface)
if info:
for system in self._systems:
system.ProcessCallback(interface, info)
else:
if 'Callback' in interface.ext_attrs:
_logger.info('Malformed callback: %s' % interface.id)
self._ProcessInterface(interface, super_interface,
source_filter, common_prefix)
# Libraries
if lib_dir:
for system in self._systems:
system.GenerateLibraries(lib_dir)
for system in self._systems:
system.Finish()
def _PreOrderInterfaces(self, interfaces):
"""Returns the interfaces in pre-order, i.e. parents first."""
seen = set()
ordered = []
def visit(interface):
if interface.id in seen:
return
seen.add(interface.id)
for parent in interface.parents:
if IsDartCollectionType(parent.type.id):
continue
if self._database.HasInterface(parent.type.id):
parent_interface = self._database.GetInterface(parent.type.id)
visit(parent_interface)
ordered.append(interface)
for interface in interfaces:
visit(interface)
return ordered
def _ProcessInterface(self, interface, super_interface_name,
source_filter,
common_prefix):
"""."""
_logger.info('Generating %s' % interface.id)
generators = [system.InterfaceGenerator(interface,
common_prefix,
super_interface_name,
source_filter)
for system in self._systems]
generators = filter(None, generators)
for generator in generators:
generator.StartInterface()
for const in sorted(interface.constants, ConstantOutputOrder):
for generator in generators:
generator.AddConstant(const)
attributes = [attr for attr in interface.attributes
if attr.type.id != 'EventListener']
for (getter, setter) in _PairUpAttributes(attributes):
for generator in generators:
generator.AddAttribute(getter, setter)
# The implementation should define an indexer if the interface directly
# extends List.
element_type = MaybeListElementType(interface)
if element_type:
for generator in generators:
generator.AddIndexer(element_type)
# 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)
for generator in generators:
generator.AddOperation(info)
# 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.
for parent_interface in self._TransitiveSecondaryParents(interface):
if isinstance(parent_interface, str): # IsDartCollectionType(parent_interface)
continue
attributes = [attr for attr in parent_interface.attributes
if not FindMatchingAttribute(interface, attr)]
for (getter, setter) in _PairUpAttributes(attributes):
for generator in generators:
generator.AddSecondaryAttribute(parent_interface, getter, setter)
# 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)
for generator in generators:
generator.AddSecondaryOperation(parent_interface, info)
for generator in generators:
generator.FinishInterface()
return
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:
if IsDartCollectionType(parent.type.id):
result.append(parent.type.id)
continue
if self._database.HasInterface(parent.type.id):
parent_interface = self._database.GetInterface(parent.type.id)
result.append(parent_interface)
walk(parent_interface.parents)
result = []
walk(interface.parents[1:])
return result;
def _CollectExceptions(self, interfaces):
"""Returns the names of all exception classes raised."""
exceptions = set()
for interface in interfaces:
for attribute in interface.attributes:
if attribute.get_raises:
exceptions.add(attribute.get_raises.id)
if attribute.set_raises:
exceptions.add(attribute.set_raises.id)
for operation in interface.operations:
if operation.raises:
exceptions.add(operation.raises.id)
return exceptions
def Flush(self):
"""Write out all pending files."""
_logger.info('Flush...')
self._emitters.Flush()
def _FixEventTargets(self):
for interface in self._database.GetInterfaces():
# Create fake EventTarget parent interface for interfaces that have
# 'EventTarget' extended attribute.
if 'EventTarget' in interface.ext_attrs:
ast = [('Annotation', [('Id', 'WebKit')]),
('InterfaceType', ('ScopedName', 'EventTarget'))]
interface.parents.append(idlnode.IDLParentInterface(ast))
def _ComputeInheritanceClosure(self):
def Collect(interface, seen, collected):
name = interface.id
if '<' in name:
# TODO(sra): Handle parameterized types.
return
if not name in seen:
seen.add(name)
collected.append(name)
for parent in interface.parents:
# TODO(sra): Handle parameterized types.
if not '<' in parent.type.id:
if self._database.HasInterface(parent.type.id):
Collect(self._database.GetInterface(parent.type.id),
seen, collected)
self._inheritance_closure = {}
for interface in self._database.GetInterfaces():
seen = set()
collected = []
Collect(interface, seen, collected)
self._inheritance_closure[interface.id] = collected
def _AllImplementedInterfaces(self, interface):
"""Returns a list of the names of all interfaces implemented by 'interface'.
List includes the name of 'interface'.
"""
return self._inheritance_closure[interface.id]
def _PairUpAttributes(attributes):
"""Returns a list of (getter, setter) pairs sorted by name.
One element of the pair may be None.
"""
names = sorted(set(attr.id for attr in attributes))
getters = {}
setters = {}
for attr in attributes:
if attr.is_fc_getter:
getters[attr.id] = attr
elif attr.is_fc_setter and 'Replaceable' not in attr.ext_attrs:
setters[attr.id] = attr
return [(getters.get(id), setters.get(id)) for id in names]
# ------------------------------------------------------------------------------
class DummyImplementationSystem(System):
"""Generates a dummy implementation for use by the editor analysis.
All the code comes from hand-written library files.
"""
def __init__(self, templates, database, emitters, output_dir):
super(DummyImplementationSystem, self).__init__(
templates, database, emitters, output_dir)
factory_providers_file = os.path.join(self._output_dir, 'src', 'dummy',
'RegularFactoryProviders.dart')
self._factory_providers_emitter = self._emitters.FileEmitter(
factory_providers_file)
self._impl_file_paths = [factory_providers_file]
def InterfaceGenerator(self,
interface,
common_prefix,
super_interface_name,
source_filter):
return DummyInterfaceGenerator(self, interface)
def ProcessCallback(self, interface, info):
pass
def GenerateLibraries(self, lib_dir):
# Library generated for implementation.
self._GenerateLibFile(
'dom_dummy.darttemplate',
os.path.join(lib_dir, 'dom_dummy.dart'),
(self._interface_system._dart_interface_file_paths +
self._interface_system._dart_callback_file_paths +
self._impl_file_paths))
# ------------------------------------------------------------------------------
class DummyInterfaceGenerator(object):
"""Generates dummy implementation."""
def __init__(self, system, interface):
self._system = system
self._interface = interface
def StartInterface(self):
# There is no implementation to match the interface, but there might be a
# factory constructor for the Dart interface.
constructor_info = AnalyzeConstructor(self._interface)
if constructor_info:
dart_interface_name = self._interface.id
self._EmitFactoryProvider(dart_interface_name, constructor_info)
def _EmitFactoryProvider(self, interface_name, constructor_info):
factory_provider = '_' + interface_name + 'FactoryProvider'
self._system._factory_providers_emitter.Emit(
self._system._templates.Load('factoryprovider.darttemplate'),
FACTORYPROVIDER=factory_provider,
CONSTRUCTOR=interface_name,
PARAMETERS=constructor_info.ParametersImplementationDeclaration())
def FinishInterface(self):
pass
def AddConstant(self, constant):
pass
def AddAttribute(self, getter, setter):
pass
def AddSecondaryAttribute(self, interface, getter, setter):
pass
def AddSecondaryOperation(self, interface, info):
pass
def AddIndexer(self, element_type):
pass
def AddTypedArrayConstructors(self, element_type):
pass
def AddOperation(self, info):
pass
def AddEventAttributes(self, event_attrs):
pass