1# Copyright (C) 2013 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29"""Generate template values for an interface. 30 31FIXME: Not currently used in build. 32This is a rewrite of the Perl IDL compiler in Python, but is not complete. 33Once it is complete, we will switch all IDL files over to Python at once. 34Until then, please work on the Perl IDL compiler. 35For details, see bug http://crbug.com/239771 36""" 37 38import v8_attributes 39from v8_globals import includes 40import v8_methods 41import v8_types 42import v8_utilities 43from v8_utilities import capitalize, conditional_string, cpp_name, has_extended_attribute, has_extended_attribute_value, runtime_enabled_function_name 44 45 46INTERFACE_H_INCLUDES = set([ 47 'bindings/v8/V8Binding.h', 48 'bindings/v8/V8DOMWrapper.h', 49 'bindings/v8/WrapperTypeInfo.h', 50]) 51INTERFACE_CPP_INCLUDES = set([ 52 'RuntimeEnabledFeatures.h', 53 'bindings/v8/ExceptionState.h', 54 'bindings/v8/V8DOMConfiguration.h', 55 'core/dom/ContextFeatures.h', 56 'core/dom/Document.h', 57 'platform/TraceEvent.h', 58]) 59 60 61def generate_interface(interface): 62 includes.clear() 63 includes.update(INTERFACE_CPP_INCLUDES) 64 header_includes = INTERFACE_H_INCLUDES 65 66 parent_interface = interface.parent 67 if parent_interface: 68 header_includes.update(v8_types.includes_for_type(parent_interface)) 69 extended_attributes = interface.extended_attributes 70 71 # [CheckSecurity] 72 is_check_security = 'CheckSecurity' in extended_attributes 73 if is_check_security: 74 includes.add('bindings/v8/BindingSecurity.h') 75 76 # [GenerateVisitDOMWrapper] 77 generate_visit_dom_wrapper_function = extended_attributes.get('GenerateVisitDOMWrapper') 78 if generate_visit_dom_wrapper_function: 79 includes.update(['bindings/v8/V8GCController.h', 80 'core/dom/Element.h']) 81 82 # [MeasureAs] 83 is_measure_as = 'MeasureAs' in extended_attributes 84 if is_measure_as: 85 includes.add('core/frame/UseCounter.h') 86 87 # [SpecialWrapFor] 88 if 'SpecialWrapFor' in extended_attributes: 89 special_wrap_for = extended_attributes['SpecialWrapFor'].split('|') 90 else: 91 special_wrap_for = [] 92 for special_wrap_interface in special_wrap_for: 93 v8_types.add_includes_for_type(special_wrap_interface) 94 95 # Constructors 96 constructors = [generate_constructor(interface, constructor) 97 for constructor in interface.constructors 98 # FIXME: shouldn't put named constructors with constructors 99 # (currently needed for Perl compatibility) 100 # Handle named constructors separately 101 if constructor.name == 'Constructor'] 102 generate_constructor_overloads(constructors) 103 104 # [CustomConstructor] 105 has_custom_constructor = 'CustomConstructor' in extended_attributes 106 107 # [EventConstructor] 108 has_event_constructor = 'EventConstructor' in extended_attributes 109 any_type_attributes = [attribute for attribute in interface.attributes 110 if attribute.idl_type == 'any'] 111 if has_event_constructor: 112 includes.add('bindings/v8/Dictionary.h') 113 if any_type_attributes: 114 includes.add('bindings/v8/SerializedScriptValue.h') 115 116 # [NamedConstructor] 117 if 'NamedConstructor' in extended_attributes: 118 # FIXME: parser should return named constructor separately; 119 # included in constructors (and only name stored in extended attribute) 120 # for Perl compatibility 121 named_constructor = {'name': extended_attributes['NamedConstructor']} 122 else: 123 named_constructor = None 124 125 if (constructors or has_custom_constructor or has_event_constructor or 126 named_constructor): 127 includes.add('bindings/v8/V8ObjectConstructor.h') 128 129 template_contents = { 130 'any_type_attributes': any_type_attributes, 131 'conditional_string': conditional_string(interface), # [Conditional] 132 'constructors': constructors, 133 'cpp_class': cpp_name(interface), 134 'generate_visit_dom_wrapper_function': generate_visit_dom_wrapper_function, 135 'has_custom_constructor': has_custom_constructor, 136 'has_custom_legacy_call_as_function': has_extended_attribute_value(interface, 'Custom', 'LegacyCallAsFunction'), # [Custom=LegacyCallAsFunction] 137 'has_custom_to_v8': has_extended_attribute_value(interface, 'Custom', 'ToV8'), # [Custom=ToV8] 138 'has_custom_wrap': has_extended_attribute_value(interface, 'Custom', 'Wrap'), # [Custom=Wrap] 139 'has_event_constructor': has_event_constructor, 140 'has_visit_dom_wrapper': ( 141 # [Custom=Wrap], [GenerateVisitDOMWrapper] 142 has_extended_attribute_value(interface, 'Custom', 'VisitDOMWrapper') or 143 'GenerateVisitDOMWrapper' in extended_attributes), 144 'header_includes': header_includes, 145 'interface_length': interface_length(interface, constructors), 146 'interface_name': interface.name, 147 'is_active_dom_object': 'ActiveDOMObject' in extended_attributes, # [ActiveDOMObject] 148 'is_check_security': is_check_security, 149 'is_constructor_call_with_document': has_extended_attribute_value( 150 interface, 'ConstructorCallWith', 'Document'), # [ConstructorCallWith=Document] 151 'is_constructor_call_with_execution_context': has_extended_attribute_value( 152 interface, 'ConstructorCallWith', 'ExecutionContext'), # [ConstructorCallWith=ExeuctionContext] 153 'is_constructor_raises_exception': extended_attributes.get('RaisesException') == 'Constructor', # [RaisesException=Constructor] 154 'is_dependent_lifetime': 'DependentLifetime' in extended_attributes, # [DependentLifetime] 155 'is_event_target': inherits_interface(interface, 'EventTarget'), 156 'is_node': inherits_interface(interface, 'Node'), 157 'measure_as': v8_utilities.measure_as(interface), # [MeasureAs] 158 'named_constructor': named_constructor, 159 'parent_interface': parent_interface, 160 'runtime_enabled_function': runtime_enabled_function_name(interface), # [RuntimeEnabled] 161 'special_wrap_for': special_wrap_for, 162 'v8_class': v8_utilities.v8_class_name(interface), 163 } 164 165 template_contents.update({ 166 'constants': [generate_constant(constant) for constant in interface.constants], 167 'do_not_check_constants': 'DoNotCheckConstants' in extended_attributes, 168 }) 169 170 attributes = [v8_attributes.generate_attribute(interface, attribute) 171 for attribute in interface.attributes] 172 template_contents.update({ 173 'attributes': attributes, 174 'has_accessors': any(attribute['is_expose_js_accessors'] for attribute in attributes), 175 'has_constructor_attributes': any(attribute['constructor_type'] for attribute in attributes), 176 'has_per_context_enabled_attributes': any(attribute['per_context_enabled_function'] for attribute in attributes), 177 'has_replaceable_attributes': any(attribute['is_replaceable'] for attribute in attributes), 178 }) 179 180 methods = [v8_methods.generate_method(interface, method) 181 for method in interface.operations] 182 generate_overloads(methods) 183 for method in methods: 184 method['do_generate_method_configuration'] = ( 185 method['do_not_check_signature'] and 186 not method['per_context_enabled_function'] and 187 # For overloaded methods, only generate one accessor 188 ('overload_index' not in method or method['overload_index'] == 1)) 189 190 template_contents.update({ 191 'has_origin_safe_method_setter': any( 192 method['is_check_security_for_frame'] and not method['is_read_only'] 193 for method in methods), 194 'has_method_configuration': any(method['do_generate_method_configuration'] for method in methods), 195 'has_per_context_enabled_methods': any(method['per_context_enabled_function'] for method in methods), 196 'methods': methods, 197 }) 198 199 return template_contents 200 201 202# [DeprecateAs], [Reflect], [RuntimeEnabled] 203def generate_constant(constant): 204 # (Blink-only) string literals are unquoted in tokenizer, must be re-quoted 205 # in C++. 206 if constant.idl_type == 'DOMString': 207 value = '"%s"' % constant.value 208 else: 209 value = constant.value 210 211 constant_parameter = { 212 'name': constant.name, 213 # FIXME: use 'reflected_name' as correct 'name' 214 'reflected_name': constant.extended_attributes.get('Reflect', constant.name), 215 'runtime_enabled_function': runtime_enabled_function_name(constant), 216 'value': value, 217 } 218 return constant_parameter 219 220 221# Overloads 222 223def generate_overloads(methods): 224 generate_overloads_by_type(methods, is_static=False) # Regular methods 225 generate_overloads_by_type(methods, is_static=True) 226 227 228def generate_overloads_by_type(methods, is_static): 229 # Generates |overloads| template values and modifies |methods| in place; 230 # |is_static| flag used (instead of partitioning list in 2) because need to 231 # iterate over original list of methods to modify in place 232 method_counts = {} 233 for method in methods: 234 if method['is_static'] != is_static: 235 continue 236 name = method['name'] 237 method_counts.setdefault(name, 0) 238 method_counts[name] += 1 239 240 # Filter to only methods that are actually overloaded 241 overloaded_method_counts = dict((name, count) 242 for name, count in method_counts.iteritems() 243 if count > 1) 244 245 # Add overload information only to overloaded methods, so template code can 246 # easily verify if a function is overloaded 247 method_overloads = {} 248 for method in methods: 249 name = method['name'] 250 if (method['is_static'] != is_static or 251 name not in overloaded_method_counts): 252 continue 253 # Overload index includes self, so first append, then compute index 254 method_overloads.setdefault(name, []).append(method) 255 method.update({ 256 'overload_index': len(method_overloads[name]), 257 'overload_resolution_expression': overload_resolution_expression(method), 258 }) 259 260 # Resolution function is generated after last overloaded function; 261 # package necessary information into |method.overloads| for that method. 262 for method in methods: 263 if (method['is_static'] != is_static or 264 'overload_index' not in method): 265 continue 266 name = method['name'] 267 if method['overload_index'] != overloaded_method_counts[name]: 268 continue 269 overloads = method_overloads[name] 270 method['overloads'] = { 271 'name': name, 272 'methods': overloads, 273 'minimum_number_of_required_arguments': min( 274 overload['number_of_required_arguments'] 275 for overload in overloads), 276 } 277 278 279def overload_resolution_expression(method): 280 # Expression is an OR of ANDs: each term in the OR corresponds to a 281 # possible argument count for a given method, with type checks. 282 # FIXME: Blink's overload resolution algorithm is incorrect, per: 283 # https://code.google.com/p/chromium/issues/detail?id=293561 284 # Properly: 285 # 1. Compute effective overload set. 286 # 2. First check type list length. 287 # 3. If multiple entries for given length, compute distinguishing argument 288 # index and have check for that type. 289 arguments = method['arguments'] 290 overload_checks = [overload_check_expression(method, index) 291 # check *omitting* optional arguments at |index| and up: 292 # index 0 => argument_count 0 (no arguments) 293 # index 1 => argument_count 1 (index 0 argument only) 294 for index, argument in enumerate(arguments) 295 if argument['is_optional']] 296 # FIXME: this is wrong if a method has optional arguments and a variadic 297 # one, though there are not yet any examples of this 298 if not method['is_variadic']: 299 # Includes all optional arguments (len = last index + 1) 300 overload_checks.append(overload_check_expression(method, len(arguments))) 301 return ' || '.join('(%s)' % check for check in overload_checks) 302 303 304def overload_check_expression(method, argument_count): 305 overload_checks = ['info.Length() == %s' % argument_count] 306 arguments = method['arguments'][:argument_count] 307 overload_checks.extend(overload_check_argument(index, argument) 308 for index, argument in 309 enumerate(arguments)) 310 return ' && '.join('(%s)' % check for check in overload_checks if check) 311 312 313def overload_check_argument(index, argument): 314 cpp_value = 'info[%s]' % index 315 idl_type = argument['idl_type'] 316 # FIXME: proper type checking, sharing code with attributes and methods 317 if idl_type == 'DOMString' and argument['is_strict_type_checking']: 318 return ' || '.join(['%s->IsNull()' % cpp_value, 319 '%s->IsUndefined()' % cpp_value, 320 '%s->IsString()' % cpp_value, 321 '%s->IsObject()' % cpp_value]) 322 if v8_types.array_or_sequence_type(idl_type): 323 return '%s->IsArray()' % cpp_value 324 if v8_types.is_wrapper_type(idl_type): 325 type_check = 'V8{idl_type}::hasInstance({cpp_value}, info.GetIsolate(), worldType(info.GetIsolate()))'.format(idl_type=idl_type, cpp_value=cpp_value) 326 if argument['is_nullable']: 327 type_check = ' || '.join(['%s->IsNull()' % cpp_value, type_check]) 328 return type_check 329 return None 330 331 332# Constructors 333 334# [Constructor] 335def generate_constructor(interface, constructor): 336 return { 337 'argument_list': constructor_argument_list(interface, constructor), 338 'arguments': [constructor_argument(argument, index) 339 for index, argument in enumerate(constructor.arguments)], 340 'is_constructor': True, 341 'is_variadic': False, # Required for overload resolution 342 'number_of_required_arguments': 343 len([argument for argument in constructor.arguments 344 if not argument.is_optional]), 345 } 346 347 348def constructor_argument_list(interface, constructor): 349 arguments = [] 350 # [ConstructorCallWith=ExecutionContext] 351 if has_extended_attribute_value(interface, 'ConstructorCallWith', 'ExecutionContext'): 352 arguments.append('context') 353 # [ConstructorCallWith=Document] 354 if has_extended_attribute_value(interface, 'ConstructorCallWith', 'Document'): 355 arguments.append('document') 356 357 arguments.extend([argument.name for argument in constructor.arguments]) 358 359 # [RaisesException=Constructor] 360 if interface.extended_attributes.get('RaisesException') == 'Constructor': 361 arguments.append('exceptionState') 362 363 return arguments 364 365 366def constructor_argument(argument, index): 367 return { 368 'has_default': 'Default' in argument.extended_attributes, 369 'idl_type': argument.idl_type, 370 'index': index, 371 'is_nullable': False, # Required for overload resolution 372 'is_optional': argument.is_optional, 373 'is_strict_type_checking': False, # Required for overload resolution 374 'name': argument.name, 375 'v8_value_to_local_cpp_value': 376 v8_methods.v8_value_to_local_cpp_value(argument, index), 377 } 378 379 380def generate_constructor_overloads(constructors): 381 if len(constructors) <= 1: 382 return 383 for overload_index, constructor in enumerate(constructors): 384 constructor.update({ 385 'overload_index': overload_index + 1, 386 'overload_resolution_expression': 387 overload_resolution_expression(constructor), 388 }) 389 390 391def interface_length(interface, constructors): 392 # Docs: http://heycam.github.io/webidl/#es-interface-call 393 if 'EventConstructor' in interface.extended_attributes: 394 return 1 395 if not constructors: 396 return 0 397 return min(constructor['number_of_required_arguments'] 398 for constructor in constructors) 399 400 401# Interface dependencies 402 403def inherits_interface(interface, ancestor): 404 # FIXME: support distant ancestors (but don't parse all ancestors!) 405 # Do by computing ancestor chain in compute_dependencies.py 406 return ancestor in [interface.name, interface.parent] 407