• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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