blob: 6893df95d2d7a818b05d7339f8c2dfd6f4e1f253 [file] [log] [blame]
#!/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 os
import re
import shutil
from generator import *
_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):
self._auxiliary_files = {}
self._dart_templates_re = re.compile(r'[\w.:]+<([\w\.<>:]+)>')
def _StripModules(self, type_name):
return type_name.split('::')[-1]
def _IsCompoundType(self, database, type_name):
if IsRegisteredType(type_name):
return True
if type_name.endswith('?'):
return self._IsCompoundType(database, type_name[:-len('?')])
if type_name.endswith('[]'):
return self._IsCompoundType(database, type_name[:-len('[]')])
stripped_type_name = self._StripModules(type_name)
if database.HasInterface(stripped_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, auxiliary_dir):
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(auxiliary_dir, Visitor, None)
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, super_database, generate_interface):
self._database = database
# Collect interfaces
interfaces = []
for interface in database.GetInterfaces():
if not MatchSourceFilter(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 dart2js 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):
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
_logger.info('Generating %s' % interface.id)
generate_interface(interface)
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 _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 FixEventTargets(self, database):
for interface in 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 AddMissingArguments(self, database):
ARG = idlnode.IDLArgument([('Type', ('ScopedName', 'object')), ('Id', 'arg')])
for interface in database.GetInterfaces():
for operation in interface.operations:
if operation.ext_attrs.get('CallWith') == 'ScriptArguments|CallStack':
operation.arguments.append(ARG)