79beeec1d4
- Parse IDL in parallel - Support calling fremontcut from dartdomgenerator to avoid reading database This speeds up the whole go.sh by about 4x on my machine. Looks like the remaining bottleneck is in the emitter. Review URL: https://codereview.chromium.org//10987042 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@13038 260f80e4-7a28-3924-810f-c04153c831b5
519 lines
17 KiB
Python
Executable File
519 lines
17 KiB
Python
Executable File
#!/usr/bin/python
|
|
# Copyright (c) 2011, 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.
|
|
|
|
import sys
|
|
|
|
|
|
class IDLNode(object):
|
|
"""Base class for all IDL elements.
|
|
IDLNode may contain various child nodes, and have properties. Examples
|
|
of IDLNode are modules, interfaces, interface members, function arguments,
|
|
etc.
|
|
"""
|
|
|
|
def __init__(self, ast):
|
|
"""Initializes an IDLNode from a PegParser AST output."""
|
|
self.id = self._find_first(ast, 'Id') if ast is not None else None
|
|
|
|
def __repr__(self):
|
|
"""Generates string of the form <class id extra extra ... 0x12345678>."""
|
|
extras = self._extra_repr()
|
|
if isinstance(extras, list):
|
|
extras = ' '.join([str(e) for e in extras])
|
|
try:
|
|
if self.id:
|
|
return '<%s %s 0x%x>' % (
|
|
type(self).__name__,
|
|
('%s %s' % (self.id, extras)).strip(),
|
|
hash(self))
|
|
return '<%s %s 0x%x>' % (
|
|
type(self).__name__,
|
|
extras,
|
|
hash(self))
|
|
except Exception, e:
|
|
return "can't convert to string: %s" % e
|
|
|
|
def _extra_repr(self):
|
|
"""Returns string of extra info for __repr__()."""
|
|
return ''
|
|
|
|
def __cmp__(self, other):
|
|
"""Override default compare operation.
|
|
IDLNodes are equal if all their properties are equal."""
|
|
if other is None or not isinstance(other, IDLNode):
|
|
return 1
|
|
return self.__dict__.__cmp__(other.__dict__)
|
|
|
|
def reset_id(self, newId):
|
|
"""Reset the id of the Node. This is typically done during a normalization
|
|
phase (e.g., "DOMWindow" -> "Window")."""
|
|
self.id = newId
|
|
|
|
def all(self, type_filter=None):
|
|
"""Returns a list containing this node and all it child nodes
|
|
(recursive).
|
|
|
|
Args:
|
|
type_filter -- can be used to limit the results to a specific
|
|
node type (e.g. IDLOperation).
|
|
"""
|
|
res = []
|
|
if type_filter is None or isinstance(self, type_filter):
|
|
res.append(self)
|
|
for v in self._all_subnodes():
|
|
if isinstance(v, IDLNode):
|
|
res.extend(v.all(type_filter))
|
|
elif isinstance(v, list):
|
|
for item in v:
|
|
if isinstance(item, IDLNode):
|
|
res.extend(item.all(type_filter))
|
|
return res
|
|
|
|
def _all_subnodes(self):
|
|
"""Accessor used by all() to find subnodes."""
|
|
return self.__dict__.values()
|
|
|
|
def to_dict(self):
|
|
"""Converts the IDLNode and its children into a dictionary.
|
|
This method is useful mostly for debugging and pretty printing.
|
|
"""
|
|
res = {}
|
|
for (k, v) in self.__dict__.items():
|
|
if v == None or v == False or v == [] or v == {}:
|
|
# Skip empty/false members.
|
|
continue
|
|
elif isinstance(v, IDLDictNode) and not len(v):
|
|
# Skip empty dict sub-nodes.
|
|
continue
|
|
elif isinstance(v, list):
|
|
# Convert lists:
|
|
new_v = []
|
|
for sub_node in v:
|
|
if isinstance(sub_node, IDLNode):
|
|
# Convert sub-node:
|
|
new_v.append(sub_node.to_dict())
|
|
else:
|
|
new_v.append(sub_node)
|
|
v = new_v
|
|
elif isinstance(v, IDLNode):
|
|
# Convert sub-node:
|
|
v = v.to_dict()
|
|
res[k] = v
|
|
return res
|
|
|
|
def _find_all(self, ast, label, max_results=sys.maxint):
|
|
"""Searches the AST for tuples with a given label. The PegParser
|
|
output is composed of lists and tuples, where the tuple 1st argument
|
|
is a label. If ast root is a list, will search recursively inside each
|
|
member in the list.
|
|
|
|
Args:
|
|
ast -- the AST to search.
|
|
label -- the label to look for.
|
|
res -- results are put into this list.
|
|
max_results -- maximum number of results.
|
|
"""
|
|
res = []
|
|
if max_results <= 0:
|
|
return res
|
|
|
|
if isinstance(ast, list):
|
|
for childAst in ast:
|
|
sub_res = self._find_all(childAst, label,
|
|
max_results - len(res))
|
|
res.extend(sub_res)
|
|
elif isinstance(ast, tuple):
|
|
(nodeLabel, value) = ast
|
|
if nodeLabel == label:
|
|
res.append(value)
|
|
return res
|
|
|
|
def _find_first(self, ast, label):
|
|
"""Convenience method for _find_all(..., max_results=1).
|
|
Returns a single element instead of a list, or None if nothing
|
|
is found."""
|
|
res = self._find_all(ast, label, max_results=1)
|
|
if len(res):
|
|
return res[0]
|
|
return None
|
|
|
|
def _has(self, ast, label):
|
|
"""Returns true if an element with the given label is
|
|
in the AST by searching for it."""
|
|
return len(self._find_all(ast, label, max_results=1)) == 1
|
|
|
|
def _convert_all(self, ast, label, idlnode_ctor):
|
|
"""Converts AST elements into IDLNode elements.
|
|
Uses _find_all to find elements with a given label and converts
|
|
them into IDLNodes with a given constructor.
|
|
Returns:
|
|
A list of the converted nodes.
|
|
Args:
|
|
ast -- the ast element to start a search at.
|
|
label -- the element label to look for.
|
|
idlnode_ctor -- a constructor function of one of the IDLNode
|
|
sub-classes.
|
|
"""
|
|
res = []
|
|
found = self._find_all(ast, label)
|
|
if not found:
|
|
return res
|
|
if not isinstance(found, list):
|
|
raise RuntimeError("Expected list but %s found" % type(found))
|
|
for childAst in found:
|
|
converted = idlnode_ctor(childAst)
|
|
res.append(converted)
|
|
return res
|
|
|
|
def _convert_first(self, ast, label, idlnode_ctor):
|
|
"""Like _convert_all, but only converts the first found results."""
|
|
childAst = self._find_first(ast, label)
|
|
if not childAst:
|
|
return None
|
|
return idlnode_ctor(childAst)
|
|
|
|
def _convert_ext_attrs(self, ast):
|
|
"""Helper method for uniform conversion of extended attributes."""
|
|
self.ext_attrs = IDLExtAttrs(ast)
|
|
|
|
def _convert_annotations(self, ast):
|
|
"""Helper method for uniform conversion of annotations."""
|
|
self.annotations = IDLAnnotations(ast)
|
|
|
|
|
|
class IDLDictNode(IDLNode):
|
|
"""Base class for dictionary-like IDL nodes such as extended attributes
|
|
and annotations. The base class implements various dict interfaces."""
|
|
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, None)
|
|
if ast is not None and isinstance(ast, dict):
|
|
self.__map = ast
|
|
else:
|
|
self.__map = {}
|
|
|
|
def __len__(self):
|
|
return len(self.__map)
|
|
|
|
def __getitem__(self, key):
|
|
return self.__map[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
self.__map[key] = value
|
|
|
|
def __delitem__(self, key):
|
|
del self.__map[key]
|
|
|
|
def __contains__(self, key):
|
|
return key in self.__map
|
|
|
|
def __iter__(self):
|
|
return self.__map.__iter__()
|
|
|
|
def get(self, key, default=None):
|
|
return self.__map.get(key, default)
|
|
|
|
def items(self):
|
|
return self.__map.items()
|
|
|
|
def keys(self):
|
|
return self.__map.keys()
|
|
|
|
def values(self):
|
|
return self.__map.values()
|
|
|
|
def clear(self):
|
|
self.__map = {}
|
|
|
|
def to_dict(self):
|
|
"""Overrides the default IDLNode.to_dict behavior.
|
|
The IDLDictNode members are copied into a new dictionary, and
|
|
IDLNode members are recursively converted into dicts as well.
|
|
"""
|
|
res = {}
|
|
for (k, v) in self.__map.items():
|
|
if isinstance(v, IDLNode):
|
|
v = v.to_dict()
|
|
res[k] = v
|
|
return res
|
|
|
|
def _all_subnodes(self):
|
|
# Usually an IDLDictNode does not contain further IDLNodes.
|
|
return []
|
|
|
|
|
|
class IDLFile(IDLNode):
|
|
"""IDLFile is the top-level node in each IDL file. It may contain
|
|
modules or interfaces."""
|
|
|
|
def __init__(self, ast, filename=None):
|
|
IDLNode.__init__(self, ast)
|
|
self.filename = filename
|
|
self.modules = self._convert_all(ast, 'Module', IDLModule)
|
|
self.interfaces = self._convert_all(ast, 'Interface', IDLInterface)
|
|
|
|
|
|
class IDLModule(IDLNode):
|
|
"""IDLModule has an id, and may contain interfaces, type defs and
|
|
implements statements."""
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self._convert_ext_attrs(ast)
|
|
self._convert_annotations(ast)
|
|
self.interfaces = self._convert_all(ast, 'Interface', IDLInterface)
|
|
self.typeDefs = self._convert_all(ast, 'TypeDef', IDLTypeDef)
|
|
self.implementsStatements = self._convert_all(ast, 'ImplStmt',
|
|
IDLImplementsStatement)
|
|
|
|
|
|
class IDLExtAttrs(IDLDictNode):
|
|
"""IDLExtAttrs is an IDLDictNode that stores IDL Extended Attributes.
|
|
Modules, interfaces, members and arguments can all own IDLExtAttrs."""
|
|
def __init__(self, ast=None):
|
|
IDLDictNode.__init__(self, None)
|
|
if not ast:
|
|
return
|
|
ext_attrs_ast = self._find_first(ast, 'ExtAttrs')
|
|
if not ext_attrs_ast:
|
|
return
|
|
for ext_attr in self._find_all(ext_attrs_ast, 'ExtAttr'):
|
|
name = self._find_first(ext_attr, 'Id')
|
|
value = self._find_first(ext_attr, 'ExtAttrValue')
|
|
|
|
func_value = self._find_first(value, 'ExtAttrFunctionValue')
|
|
if func_value:
|
|
# E.g. NamedConstructor=Audio(in [Optional] DOMString src)
|
|
self[name] = IDLExtAttrFunctionValue(
|
|
func_value,
|
|
self._find_first(func_value, 'ExtAttrArgList'))
|
|
continue
|
|
|
|
ctor_args = not value and self._find_first(ext_attr, 'ExtAttrArgList')
|
|
if ctor_args:
|
|
# E.g. Constructor(Element host)
|
|
self[name] = IDLExtAttrFunctionValue(None, ctor_args)
|
|
continue
|
|
|
|
self[name] = value
|
|
|
|
def _all_subnodes(self):
|
|
# Extended attributes may contain IDLNodes, e.g. IDLExtAttrFunctionValue
|
|
return self.values()
|
|
|
|
|
|
class IDLExtAttrFunctionValue(IDLNode):
|
|
"""IDLExtAttrFunctionValue."""
|
|
def __init__(self, func_value_ast, arg_list_ast):
|
|
IDLNode.__init__(self, func_value_ast)
|
|
self.arguments = self._convert_all(arg_list_ast, 'Argument', IDLArgument)
|
|
|
|
|
|
class IDLType(IDLNode):
|
|
"""IDLType is used to describe constants, attributes and operations'
|
|
return and input types. IDLType matches AST labels such as ScopedName,
|
|
StringType, VoidType, IntegerType, etc."""
|
|
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
# Search for a 'ScopedName' or any label ending with 'Type'.
|
|
if isinstance(ast, list):
|
|
self.id = self._find_first(ast, 'ScopedName')
|
|
if not self.id:
|
|
# FIXME: use regexp search instead
|
|
def findType(ast):
|
|
for label, childAst in ast:
|
|
if label.endswith('Type'):
|
|
type = self._label_to_type(label, ast)
|
|
if type != 'sequence':
|
|
return type
|
|
type_ast = self._find_first(childAst, 'Type')
|
|
if not type_ast:
|
|
return type
|
|
return 'sequence<%s>' % findType(type_ast)
|
|
raise Exception('No type declaration found in %s' % ast)
|
|
self.id = findType(ast)
|
|
array_modifiers = self._find_first(ast, 'ArrayModifiers')
|
|
if array_modifiers:
|
|
self.id += array_modifiers
|
|
elif isinstance(ast, tuple):
|
|
(label, value) = ast
|
|
if label == 'ScopedName':
|
|
self.id = value
|
|
else:
|
|
self.id = self._label_to_type(label, ast)
|
|
elif isinstance(ast, str):
|
|
self.id = ast
|
|
if not self.id:
|
|
raise SyntaxError('Could not parse type %s' % (ast))
|
|
|
|
def _label_to_type(self, label, ast):
|
|
if label == 'LongLongType':
|
|
label = 'long long'
|
|
elif label.endswith('Type'):
|
|
# Omit 'Type' suffix and lowercase the rest.
|
|
label = '%s%s' % (label[0].lower(), label[1:-4])
|
|
|
|
# Add unsigned qualifier.
|
|
if self._has(ast, 'Unsigned'):
|
|
label = 'unsigned %s' % label
|
|
return label
|
|
|
|
|
|
class IDLTypeDef(IDLNode):
|
|
"""IDLNode for 'typedef [type] [id]' declarations."""
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self._convert_annotations(ast)
|
|
self.type = self._convert_first(ast, 'Type', IDLType)
|
|
|
|
|
|
class IDLInterface(IDLNode):
|
|
"""IDLInterface node contains operations, attributes, constants,
|
|
as well as parent references."""
|
|
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self._convert_ext_attrs(ast)
|
|
self._convert_annotations(ast)
|
|
self.parents = self._convert_all(ast, 'ParentInterface',
|
|
IDLParentInterface)
|
|
self.javascript_binding_name = self.id
|
|
self.doc_js_name = self.id
|
|
self.operations = self._convert_all(ast, 'Operation',
|
|
lambda ast: IDLOperation(ast, self.doc_js_name))
|
|
self.attributes = self._convert_all(ast, 'Attribute',
|
|
lambda ast: IDLAttribute(ast, self.doc_js_name))
|
|
self.constants = self._convert_all(ast, 'Const',
|
|
lambda ast: IDLConstant(ast, self.doc_js_name))
|
|
self.is_supplemental = 'Supplemental' in self.ext_attrs
|
|
self.is_no_interface_object = 'NoInterfaceObject' in self.ext_attrs
|
|
self.is_fc_suppressed = 'Suppressed' in self.ext_attrs
|
|
|
|
def reset_id(self, new_id):
|
|
"""Reset the id of the Interface and corresponding the JS names."""
|
|
if self.id != new_id:
|
|
self.id = new_id
|
|
self.doc_js_name = new_id
|
|
self.javascript_binding_name = new_id
|
|
for member in self.operations:
|
|
member.doc_js_interface_name = new_id
|
|
for member in self.attributes:
|
|
member.doc_js_interface_name = new_id
|
|
for member in self.constants:
|
|
member.doc_js_interface_name = new_id
|
|
|
|
def has_attribute(self, candidate):
|
|
for attribute in self.attributes:
|
|
if (attribute.id == candidate.id and
|
|
attribute.is_read_only == candidate.is_read_only):
|
|
return True
|
|
return False
|
|
|
|
|
|
class IDLParentInterface(IDLNode):
|
|
"""This IDLNode specialization is for 'Interface Child : Parent {}'
|
|
declarations."""
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self._convert_annotations(ast)
|
|
self.type = self._convert_first(ast, 'InterfaceType', IDLType)
|
|
|
|
|
|
class IDLMember(IDLNode):
|
|
"""A base class for constants, attributes and operations."""
|
|
|
|
def __init__(self, ast, doc_js_interface_name):
|
|
IDLNode.__init__(self, ast)
|
|
self.type = self._convert_first(ast, 'Type', IDLType)
|
|
self._convert_ext_attrs(ast)
|
|
self._convert_annotations(ast)
|
|
self.doc_js_interface_name = doc_js_interface_name
|
|
self.is_fc_suppressed = 'Suppressed' in self.ext_attrs
|
|
self.is_static = self._has(ast, 'Static')
|
|
|
|
|
|
class IDLOperation(IDLMember):
|
|
"""IDLNode specialization for 'type name(args)' declarations."""
|
|
def __init__(self, ast, doc_js_interface_name):
|
|
IDLMember.__init__(self, ast, doc_js_interface_name)
|
|
self.type = self._convert_first(ast, 'ReturnType', IDLType)
|
|
self.arguments = self._convert_all(ast, 'Argument', IDLArgument)
|
|
self.raises = self._convert_first(ast, 'Raises', IDLType)
|
|
self.specials = self._find_all(ast, 'Special')
|
|
self.is_stringifier = self._has(ast, 'Stringifier')
|
|
def _extra_repr(self):
|
|
return [self.arguments]
|
|
|
|
|
|
class IDLAttribute(IDLMember):
|
|
"""IDLNode specialization for 'attribute type name' declarations."""
|
|
def __init__(self, ast, doc_js_interface_name):
|
|
IDLMember.__init__(self, ast, doc_js_interface_name)
|
|
self.is_read_only = self._has(ast, 'ReadOnly')
|
|
# There are various ways to define exceptions for attributes:
|
|
self.raises = self._convert_first(ast, 'Raises', IDLType)
|
|
self.get_raises = self.raises \
|
|
or self._convert_first(ast, 'GetRaises', IDLType)
|
|
self.set_raises = self.raises \
|
|
or self._convert_first(ast, 'SetRaises', IDLType)
|
|
def _extra_repr(self):
|
|
extra = []
|
|
if self.is_read_only: extra.append('readonly')
|
|
if self.raises: extra.append('raises')
|
|
return extra
|
|
|
|
class IDLConstant(IDLMember):
|
|
"""IDLNode specialization for 'const type name = value' declarations."""
|
|
def __init__(self, ast, doc_js_interface_name):
|
|
IDLMember.__init__(self, ast, doc_js_interface_name)
|
|
self.value = self._find_first(ast, 'ConstExpr')
|
|
|
|
|
|
class IDLArgument(IDLNode):
|
|
"""IDLNode specialization for operation arguments."""
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self.type = self._convert_first(ast, 'Type', IDLType)
|
|
self._convert_ext_attrs(ast)
|
|
|
|
def __repr__(self):
|
|
return '<IDLArgument(type = %s, id = %s)>' % (self.type, self.id)
|
|
|
|
|
|
class IDLImplementsStatement(IDLNode):
|
|
"""IDLNode specialization for 'X implements Y' declarations."""
|
|
def __init__(self, ast):
|
|
IDLNode.__init__(self, ast)
|
|
self.implementor = self._convert_first(ast, 'ImplStmtImplementor',
|
|
IDLType)
|
|
self.implemented = self._convert_first(ast, 'ImplStmtImplemented',
|
|
IDLType)
|
|
|
|
|
|
class IDLAnnotations(IDLDictNode):
|
|
"""IDLDictNode specialization for a list of FremontCut annotations."""
|
|
def __init__(self, ast=None):
|
|
IDLDictNode.__init__(self, ast)
|
|
self.id = None
|
|
if not ast:
|
|
return
|
|
for annotation in self._find_all(ast, 'Annotation'):
|
|
name = self._find_first(annotation, 'Id')
|
|
value = IDLAnnotation(annotation)
|
|
self[name] = value
|
|
|
|
|
|
class IDLAnnotation(IDLDictNode):
|
|
"""IDLDictNode specialization for one annotation."""
|
|
def __init__(self, ast=None):
|
|
IDLDictNode.__init__(self, ast)
|
|
self.id = None
|
|
if not ast:
|
|
return
|
|
for arg in self._find_all(ast, 'AnnotationArg'):
|
|
name = self._find_first(arg, 'Id')
|
|
value = self._find_first(arg, 'AnnotationArgValue')
|
|
self[name] = value
|