summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/blink/renderer/bindings/scripts/v8_utilities.py
blob: 2ecd69233208b68d0bb573c5f8f2bbe5e4a455b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#     * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Functions shared by various parts of the code generator.

Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
"""

import os
import re
import sys

from blinkbuild.name_style_converter import NameStyleConverter
from idl_types import IdlTypeBase
import idl_types
from idl_definitions import Exposure, IdlInterface, IdlAttribute
from utilities import to_snake_case
from v8_globals import includes

ACRONYMS = [
    'CSSOM',  # must come *before* CSS to match full acronym
    'CSS',
    'HTML',
    'IME',
    'JS',
    'SMIL',
    'SVG',
    'URL',
    'WOFF',
    'XML',
    'XSLT',
]

################################################################################
# Extended attribute parsing
################################################################################


def extended_attribute_value_contains(extended_attribute_value, key):
    return (extended_attribute_value == key
            or (isinstance(extended_attribute_value, list)
                and key in extended_attribute_value))


def has_extended_attribute(definition_or_member, extended_attribute_list):
    return any(extended_attribute in definition_or_member.extended_attributes
               for extended_attribute in extended_attribute_list)


def has_extended_attribute_value(definition_or_member, name, value):
    extended_attributes = definition_or_member.extended_attributes
    return (name in extended_attributes and extended_attribute_value_contains(
        extended_attributes[name], value))


def extended_attribute_value_as_list(definition_or_member, name):
    extended_attributes = definition_or_member.extended_attributes
    if name not in extended_attributes:
        return None
    value = extended_attributes[name]
    if isinstance(value, list):
        return value
    return [value]


################################################################################
# String handling
################################################################################


def capitalize(name):
    """Capitalize first letter or initial acronym (used in setter names)."""
    for acronym in ACRONYMS:
        if name.startswith(acronym.lower()):
            return name.replace(acronym.lower(), acronym)
    return name[0].upper() + name[1:]


def strip_suffix(string, suffix):
    if not suffix or not string.endswith(suffix):
        return string
    return string[:-len(suffix)]


def uncapitalize(name):
    """Uncapitalizes first letter or initial acronym (used in method names).

    E.g., 'SetURL' becomes 'setURL', but 'URLFoo' becomes 'urlFoo'.
    """
    for acronym in ACRONYMS:
        if name.startswith(acronym):
            return name.replace(acronym, acronym.lower())
    return name[0].lower() + name[1:]


def runtime_enabled_function(name):
    """Returns a function call of a runtime enabled feature."""
    return 'RuntimeEnabledFeatures::%sEnabled()' % name


################################################################################
# C++
################################################################################


def scoped_name(interface, definition, base_name):
    # partial interfaces are implemented as separate classes, with their members
    # implemented as static member functions
    partial_interface_implemented_as = definition.extended_attributes.get(
        'PartialInterfaceImplementedAs')
    if partial_interface_implemented_as:
        return '%s::%s' % (partial_interface_implemented_as, base_name)
    if (definition.is_static
            or definition.name in ('Constructor', 'NamedConstructor')):
        return '%s::%s' % (cpp_name(interface), base_name)
    return 'impl->%s' % base_name


def v8_class_name(interface):
    return 'V8' + interface.name


def v8_class_name_or_partial(interface):
    class_name = v8_class_name(interface)
    if interface.is_partial:
        return ''.join([class_name, 'Partial'])
    return class_name


def build_basename(name, prefix=None):
    basename = to_snake_case(name)
    if prefix:
        basename = prefix + basename
    return basename


def binding_header_filename(name):
    """Returns a binding header filename for the specified interface name.

    E.g. 'NodeList' -> 'v8_node_list.h'
    """
    return build_basename(name, prefix='v8_') + '.h'


################################################################################
# Specific extended attributes
################################################################################


# [ActivityLogging]
def activity_logging_world_list(member, access_type=''):
    """Returns a set of world suffixes for which a definition member has activity logging, for specified access type.

    access_type can be 'Getter' or 'Setter' if only checking getting or setting.
    """
    extended_attributes = member.extended_attributes
    if 'LogActivity' not in extended_attributes:
        return set()
    log_activity = extended_attributes['LogActivity']
    if log_activity and not log_activity.startswith(access_type):
        return set()

    includes.add('platform/bindings/v8_dom_activity_logger.h')
    if 'LogAllWorlds' in extended_attributes:
        return set(['', 'ForMainWorld'])
    return set([''])  # At minimum, include isolated worlds.


# [ActivityLogging]
def activity_logging_world_check(member):
    """Returns if an isolated world check is required when generating activity
    logging code.

    The check is required when there is no per-world binding code and logging is
    required only for isolated world.
    """
    extended_attributes = member.extended_attributes
    if 'LogActivity' not in extended_attributes:
        return False
    if ('PerWorldBindings' not in extended_attributes
            and 'LogAllWorlds' not in extended_attributes):
        return True
    return False


# [CallWith]
CALL_WITH_ARGUMENTS = {
    'Isolate': 'info.GetIsolate()',
    'ScriptState': 'script_state',
    'ExecutionContext': 'execution_context',
    'Document': 'document',
    'ThisValue': 'ScriptValue(info.GetIsolate(), info.Holder())',
}
# List because key order matters, as we want arguments in deterministic order
CALL_WITH_VALUES = [
    'Isolate',
    'ScriptState',
    'ExecutionContext',
    'Document',
    'ThisValue',
]


def call_with_arguments(call_with_values):
    if not call_with_values:
        return []
    return [
        CALL_WITH_ARGUMENTS[value] for value in CALL_WITH_VALUES
        if extended_attribute_value_contains(call_with_values, value)
    ]


# [Constructor], [NamedConstructor]
def is_constructor_attribute(member):
    # TODO(yukishiino): replace this with [Constructor] and [NamedConstructor] extended attribute
    return (type(member) == IdlAttribute
            and member.idl_type.name.endswith('Constructor'))


# [DeprecateAs]
def deprecate_as(member):
    extended_attributes = member.extended_attributes
    if 'DeprecateAs' not in extended_attributes:
        return None
    includes.add('core/frame/deprecation.h')
    return extended_attributes['DeprecateAs']


# [Exposed]
EXPOSED_EXECUTION_CONTEXT_METHOD = {
    'AnimationWorklet': 'IsAnimationWorkletGlobalScope',
    'AudioWorklet': 'IsAudioWorkletGlobalScope',
    'DedicatedWorker': 'IsDedicatedWorkerGlobalScope',
    'LayoutWorklet': 'IsLayoutWorkletGlobalScope',
    'PaintWorklet': 'IsPaintWorkletGlobalScope',
    'ServiceWorker': 'IsServiceWorkerGlobalScope',
    'SharedWorker': 'IsSharedWorkerGlobalScope',
    'Window': 'IsWindow',
    'Worker': 'IsWorkerGlobalScope',
    'Worklet': 'IsWorkletGlobalScope',
}

EXPOSED_WORKERS = set([
    'DedicatedWorker',
    'SharedWorker',
    'ServiceWorker',
])


class ExposureSet:
    """An ExposureSet is a collection of Exposure instructions."""

    def __init__(self, exposures=None):
        self.exposures = set(exposures) if exposures else set()

    def issubset(self, other):
        """Returns true if |self|'s exposure set is a subset of
        |other|'s exposure set. This function doesn't care about
        RuntimeEnabled."""
        self_set = self._extended(set(e.exposed for e in self.exposures))
        other_set = self._extended(set(e.exposed for e in other.exposures))
        return self_set.issubset(other_set)

    @staticmethod
    def _extended(target):
        if EXPOSED_WORKERS.issubset(target):
            return target | set(['Worker'])
        elif 'Worker' in target:
            return target | EXPOSED_WORKERS
        return target

    def add(self, exposure):
        self.exposures.add(exposure)

    def __len__(self):
        return len(self.exposures)

    def __iter__(self):
        return self.exposures.__iter__()

    @staticmethod
    def _code(exposure):
        condition = ('execution_context->%s()' %
                     EXPOSED_EXECUTION_CONTEXT_METHOD[exposure.exposed])
        if exposure.runtime_enabled is not None:
            runtime_enabled = (runtime_enabled_function(
                exposure.runtime_enabled))
            return '({0} && {1})'.format(condition, runtime_enabled)
        return condition

    def code(self):
        if len(self.exposures) == 0:
            return None
        # We use sorted here to deflake output.
        return ' || '.join(sorted(self._code(e) for e in self.exposures))


def exposed(member, interface):
    """Returns a C++ code that checks if a method/attribute/etc is exposed.

    When the Exposed attribute contains RuntimeEnabledFeatures (i.e.
    Exposed(Arguments) form is given), the code contains check for them as
    well.

    EXAMPLE: [Exposed=Window, RuntimeEnabledFeature=Feature1]
      => context->IsWindow()

    EXAMPLE: [Exposed(Window Feature1, Window Feature2)]
      => context->IsWindow() && RuntimeEnabledFeatures::Feature1Enabled() ||
         context->IsWindow() && RuntimeEnabledFeatures::Feature2Enabled()
    """
    exposure_set = ExposureSet(
        extended_attribute_value_as_list(member, 'Exposed'))
    interface_exposure_set = ExposureSet(
        extended_attribute_value_as_list(interface, 'Exposed'))
    for e in exposure_set:
        if e.exposed not in EXPOSED_EXECUTION_CONTEXT_METHOD:
            raise ValueError('Invalid execution context: %s' % e.exposed)

    # Methods must not be exposed to a broader scope than their interface.
    if not exposure_set.issubset(interface_exposure_set):
        raise ValueError(
            'Interface members\' exposure sets must be a subset of the interface\'s.'
        )

    return exposure_set.code()


# [SecureContext]
def secure_context(member, interface):
    """Returns C++ code that checks whether an interface/method/attribute/etc. is exposed
    to the current context. Requires that the surrounding code defines an |is_secure_context|
    variable prior to this check."""
    member_is_secure_context = 'SecureContext' in member.extended_attributes
    interface_is_secure_context = (
        (member.defined_in is None or member.defined_in == interface.name)
        and 'SecureContext' in interface.extended_attributes)

    if not (member_is_secure_context or interface_is_secure_context):
        return None

    conditions = ['is_secure_context']

    if member_is_secure_context:
        conditional = member.extended_attributes['SecureContext']
        if conditional:
            conditions.append('!{}'.format(
                runtime_enabled_function(conditional)))

    if interface_is_secure_context:
        conditional = interface.extended_attributes['SecureContext']
        if conditional:
            conditions.append('!{}'.format(
                runtime_enabled_function(conditional)))

    return ' || '.join(conditions)


# [ImplementedAs]
def cpp_name(definition_or_member):
    extended_attributes = definition_or_member.extended_attributes
    if extended_attributes and 'ImplementedAs' in extended_attributes:
        return extended_attributes['ImplementedAs']
    # WebIDL identifiers can contain hyphens[1], but C++ identifiers cannot.
    # Therefore camelCase hyphen-containing identifiers.
    #
    # [1] https://heycam.github.io/webidl/#prod-identifier
    if '-' in definition_or_member.name:
        return NameStyleConverter(
            definition_or_member.name).to_lower_camel_case()
    return definition_or_member.name


def cpp_name_from_interfaces_info(name, interfaces_info):
    return interfaces_info.get(name, {}).get('implemented_as') or name


def cpp_name_or_partial(interface):
    cpp_class_name = cpp_name(interface)
    if interface.is_partial:
        return ''.join([cpp_class_name, 'Partial'])
    return cpp_class_name


def cpp_encoded_property_name(member):
    """
    Returns a property name that the bindings generator can use in generated
    code internally.

    Note that Web IDL allows '-' (hyphen-minus) and '_' (low line) in
    identifiers but C++ does not allow or recommend them.  This function
    encodes these characters.
    """
    property_name = member.name
    # We're optimistic about name conflict.  It's highly unlikely that these
    # replacements will cause a conflict.
    assert "Dec45" not in property_name
    assert "Dec95" not in property_name
    property_name = property_name.replace("-", "Dec45")
    property_name = property_name.replace("_", "Dec95")
    return property_name


# [MeasureAs]
def measure_as(definition_or_member, interface):
    extended_attributes = definition_or_member.extended_attributes
    if 'MeasureAs' in extended_attributes:
        includes.add('core/frame/web_feature.h')
        includes.add('platform/instrumentation/use_counter.h')
        return lambda suffix: extended_attributes['MeasureAs']
    if 'Measure' in extended_attributes:
        includes.add('core/frame/web_feature.h')
        includes.add('platform/instrumentation/use_counter.h')
        measure_as_name = capitalize(definition_or_member.name)
        if interface is not None:
            measure_as_name = '%s_%s' % (capitalize(interface.name),
                                         measure_as_name)
        return lambda suffix: 'V8%s_%s' % (measure_as_name, suffix)
    return None


# [HighEntropy]
def high_entropy(definition_or_member):
    extended_attributes = definition_or_member.extended_attributes
    if 'HighEntropy' in extended_attributes:
        includes.add('core/frame/dactyloscoper.h')
        if not ('Measure' in extended_attributes
                or 'MeasureAs' in extended_attributes):
            raise Exception(
                '%s specified [HighEntropy], but does not include '
                'either [Measure] or [MeasureAs]' % definition_or_member.name)
        if extended_attributes['HighEntropy'] == 'Direct':
            return 'Direct'
        return True
    return False


# [RuntimeEnabled]
def _is_origin_trial_feature(feature_name, runtime_features):
    assert feature_name in runtime_features, feature_name + ' is not a runtime feature.'
    feature = runtime_features[feature_name]
    return feature['in_origin_trial']


def origin_trial_feature_name(definition_or_member, runtime_features):
    """
    Returns the name of the origin trial feature if found, None otherwise.
    Looks for origin trial feature specified by the RuntimeEnabled attribute.
    """
    extended_attributes = definition_or_member.extended_attributes
    feature_name = extended_attributes.get('RuntimeEnabled')
    if feature_name and _is_origin_trial_feature(feature_name,
                                                 runtime_features):
        return feature_name


def origin_trial_function_call(feature_name, execution_context=None):
    """Returns a function call to determine if an origin trial is enabled."""
    return 'RuntimeEnabledFeatures::{feature_name}Enabled({context})'.format(
        feature_name=feature_name,
        context=execution_context
        if execution_context else "execution_context")


# [ContextEnabled]
def context_enabled_feature_name(definition_or_member):
    return definition_or_member.extended_attributes.get('ContextEnabled')


# [RuntimeCallStatsCounter]
def rcs_counter_name(member, generic_counter_name):
    extended_attribute_defined = 'RuntimeCallStatsCounter' in member.extended_attributes
    if extended_attribute_defined:
        counter = 'k' + member.extended_attributes['RuntimeCallStatsCounter']
    else:
        counter = generic_counter_name
    return (counter, extended_attribute_defined)


# [RuntimeEnabled]
def runtime_enabled_feature_name(definition_or_member, runtime_features):
    extended_attributes = definition_or_member.extended_attributes
    feature_name = extended_attributes.get('RuntimeEnabled')
    if feature_name and not _is_origin_trial_feature(feature_name,
                                                     runtime_features):
        includes.add('platform/runtime_enabled_features.h')
        return feature_name


# [LegacyUnforgeable]
def is_unforgeable(member):
    return 'LegacyUnforgeable' in member.extended_attributes


# [LegacyUnforgeable], [Global]
def on_instance(interface, member):
    """Returns True if the interface's member needs to be defined on every
    instance object.

    The following members must be defined on an instance object.
    - [LegacyUnforgeable] members
    - regular members of [Global] interfaces
    """
    if member.is_static:
        return False

    # TODO(yukishiino): Remove a hack for ToString once we support
    # Symbol.ToStringTag.
    if interface.name == 'Window' and member.name == 'ToString':
        return False

    # TODO(yukishiino): Implement "interface object" and its [[Call]] method
    # in a better way.  Then we can get rid of this hack.
    if is_constructor_attribute(member):
        return True

    if ('Global' in interface.extended_attributes
            or 'LegacyUnforgeable' in member.extended_attributes):
        return True
    return False


def on_prototype(interface, member):
    """Returns True if the interface's member needs to be defined on the
    prototype object.

    Most members are defined on the prototype object.  Exceptions are as
    follows.
    - static members (optional)
    - [LegacyUnforgeable] members
    - members of [Global] interfaces
    - named properties of [Global] interfaces
    """
    if member.is_static:
        return False

    # TODO(yukishiino): Remove a hack for toString once we support
    # Symbol.toStringTag.
    if (interface.name == 'Window' and member.name == 'toString'):
        return True

    # TODO(yukishiino): Implement "interface object" and its [[Call]] method
    # in a better way.  Then we can get rid of this hack.
    if is_constructor_attribute(member):
        return False

    if ('Global' in interface.extended_attributes
            or 'LegacyUnforgeable' in member.extended_attributes):
        return False
    return True


# static, const
def on_interface(interface, member):
    """Returns True if the interface's member needs to be defined on the
    interface object.

    The following members must be defiend on an interface object.
    - static members
    """
    if member.is_static:
        return True
    return False


################################################################################
# Indexed properties
# http://heycam.github.io/webidl/#idl-indexed-properties
################################################################################


def indexed_property_getter(interface):
    try:
        # Find indexed property getter, if present; has form:
        # getter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1)
        return next(
            method for method in interface.operations
            if ('getter' in method.specials and len(method.arguments) == 1
                and str(method.arguments[0].idl_type) == 'unsigned long'))
    except StopIteration:
        return None


def indexed_property_setter(interface):
    try:
        # Find indexed property setter, if present; has form:
        # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1, ARG_TYPE ARG2)
        return next(
            method for method in interface.operations
            if ('setter' in method.specials and len(method.arguments) == 2
                and str(method.arguments[0].idl_type) == 'unsigned long'))
    except StopIteration:
        return None


def indexed_property_deleter(interface):
    try:
        # Find indexed property deleter, if present; has form:
        # deleter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG)
        return next(
            method for method in interface.operations
            if ('deleter' in method.specials and len(method.arguments) == 1
                and str(method.arguments[0].idl_type) == 'unsigned long'))
    except StopIteration:
        return None


################################################################################
# Named properties
# http://heycam.github.io/webidl/#idl-named-properties
################################################################################


def named_property_getter(interface):
    try:
        # Find named property getter, if present; has form:
        # getter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1)
        getter = next(
            method for method in interface.operations
            if ('getter' in method.specials and len(method.arguments) == 1
                and str(method.arguments[0].idl_type) == 'DOMString'))
        getter.name = getter.name or 'AnonymousNamedGetter'
        return getter
    except StopIteration:
        return None


def named_property_setter(interface):
    try:
        # Find named property setter, if present; has form:
        # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1, ARG_TYPE ARG2)
        return next(
            method for method in interface.operations
            if ('setter' in method.specials and len(method.arguments) == 2
                and str(method.arguments[0].idl_type) == 'DOMString'))
    except StopIteration:
        return None


def named_property_deleter(interface):
    try:
        # Find named property deleter, if present; has form:
        # deleter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG)
        return next(
            method for method in interface.operations
            if ('deleter' in method.specials and len(method.arguments) == 1
                and str(method.arguments[0].idl_type) == 'DOMString'))
    except StopIteration:
        return None


IdlInterface.indexed_property_getter = property(indexed_property_getter)
IdlInterface.indexed_property_setter = property(indexed_property_setter)
IdlInterface.indexed_property_deleter = property(indexed_property_deleter)
IdlInterface.named_property_getter = property(named_property_getter)
IdlInterface.named_property_setter = property(named_property_setter)
IdlInterface.named_property_deleter = property(named_property_deleter)