import logging
import os.path
import re
from string import Template
from generator_templates import GeneratorTemplates as Templates
from models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks
log = logging.getLogger('global')
def ucfirst(str):
return str[:1].upper() + str[1:]
_ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS = set(['API', 'CSS', 'DOM', 'HTML', 'XHR', 'XML'])
_TYPES_NEEDING_RUNTIME_CASTS = set([
"Runtime.RemoteObject",
"Runtime.PropertyDescriptor",
"Runtime.InternalPropertyDescriptor",
"Runtime.CollectionEntry",
"Debugger.FunctionDetails",
"Debugger.CallFrame",
"Canvas.TraceLog",
"Canvas.ResourceInfo",
"Canvas.ResourceState",
"Timeline.TimelineEvent",
"Test.TypeNeedingCast"
])
_TYPES_WITH_OPEN_FIELDS = set([
"Timeline.TimelineEvent",
"CSS.CSSProperty",
"Network.Response",
"Test.OpenParameterBundle"
])
class Generator:
def __init__(self, model, input_filepath):
self._model = model
self._input_filepath = input_filepath
def model(self):
return self._model
def generate_license(self):
return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath))
def non_supplemental_domains(self):
return filter(lambda domain: not domain.is_supplemental, self.model().domains)
def domains_to_generate(self):
return self.non_supplemental_domains()
def generate_output(self):
pass
def output_filename(self):
pass
def encoding_for_enum_value(self, enum_value):
if not hasattr(self, "_assigned_enum_values"):
self._traverse_and_assign_enum_values()
return self._enum_value_encodings[enum_value]
def assigned_enum_values(self):
if not hasattr(self, "_assigned_enum_values"):
self._traverse_and_assign_enum_values()
return self._assigned_enum_values[:]
@staticmethod
def type_needs_runtime_casts(_type):
return _type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS
@staticmethod
def type_has_open_fields(_type):
return _type.qualified_name() in _TYPES_WITH_OPEN_FIELDS
def type_needs_shape_assertions(self, _type):
if not hasattr(self, "_types_needing_shape_assertions"):
self.calculate_types_requiring_shape_assertions(self.model().domains)
return _type in self._types_needing_shape_assertions
def calculate_types_requiring_shape_assertions(self, domains):
domain_names = map(lambda domain: domain.domain_name, domains)
log.debug("> Calculating types that need shape assertions (eligible domains: %s)" % ", ".join(domain_names))
def gather_transitively_referenced_types(_type, gathered_types):
if _type in gathered_types:
return
if isinstance(_type, ObjectType):
log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name())
gathered_types.add(_type)
for type_member in _type.members:
gather_transitively_referenced_types(type_member.type, gathered_types)
elif isinstance(_type, EnumType):
log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name())
gathered_types.add(_type)
elif isinstance(_type, AliasedType):
gather_transitively_referenced_types(_type.aliased_type, gathered_types)
elif isinstance(_type, ArrayType):
gather_transitively_referenced_types(_type.element_type, gathered_types)
found_types = set()
for domain in domains:
for declaration in domain.type_declarations:
if declaration.type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS:
log.debug("> Gathering types referenced by %s to generate shape assertions." % declaration.type.qualified_name())
gather_transitively_referenced_types(declaration.type, found_types)
self._types_needing_shape_assertions = found_types
def _traverse_and_assign_enum_values(self):
self._enum_value_encodings = {}
self._assigned_enum_values = []
all_types = []
domains = self.non_supplemental_domains()
for domain in domains:
for type_declaration in domain.type_declarations:
all_types.append(type_declaration.type)
for type_member in type_declaration.type_members:
all_types.append(type_member.type)
for domain in domains:
for event in domain.events:
all_types.extend([parameter.type for parameter in event.event_parameters])
for domain in domains:
for command in domain.commands:
all_types.extend([parameter.type for parameter in command.call_parameters])
all_types.extend([parameter.type for parameter in command.return_parameters])
for _type in all_types:
if not isinstance(_type, EnumType):
continue
map(self._assign_encoding_for_enum_value, _type.enum_values())
def _assign_encoding_for_enum_value(self, enum_value):
if enum_value in self._enum_value_encodings:
return
self._enum_value_encodings[enum_value] = len(self._assigned_enum_values)
self._assigned_enum_values.append(enum_value)
def wrap_with_guard_for_domain(self, domain, text):
if self.model().framework is Frameworks.WebInspector:
return text
guard = domain.feature_guard
if guard:
return Generator.wrap_with_guard(guard, text)
return text
@staticmethod
def wrap_with_guard(guard, text):
return '\n'.join([
'#if %s' % guard,
text,
'#endif // %s' % guard,
])
@staticmethod
def stylized_name_for_enum_value(enum_value):
regex = '(%s)' % "|".join(_ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS)
def replaceCallback(match):
return match.group(1).upper()
subwords = map(ucfirst, enum_value.split('-'))
return re.sub(re.compile(regex, re.IGNORECASE), replaceCallback, "".join(subwords))
@staticmethod
def js_name_for_parameter_type(_type):
_type = _type
if isinstance(_type, AliasedType):
_type = _type.aliased_type if isinstance(_type, EnumType):
_type = _type.primitive_type
if isinstance(_type, (ArrayType, ObjectType)):
return 'object'
if isinstance(_type, PrimitiveType):
if _type.qualified_name() in ['object', 'any']:
return 'object'
elif _type.qualified_name() in ['integer', 'number']:
return 'number'
else:
return _type.qualified_name()