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"""Functions shared by various parts of the code generator.
30
31Extends IdlTypeBase type with |enum_validation_expression| property.
32
33Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
34"""
35
36import re
37
38from idl_types import IdlTypeBase
39import idl_types
40from v8_globals import includes
41import v8_types
42
43ACRONYMS = [
44    'CSSOM',  # must come *before* CSS to match full acronym
45    'CSS',
46    'HTML',
47    'IME',
48    'JS',
49    'SVG',
50    'URL',
51    'WOFF',
52    'XML',
53    'XSLT',
54]
55
56
57################################################################################
58# Extended attribute parsing
59################################################################################
60
61def extended_attribute_value_contains(extended_attribute_value, key):
62    return (extended_attribute_value == key or
63            (isinstance(extended_attribute_value, list) and
64             key in extended_attribute_value))
65
66
67def has_extended_attribute(definition_or_member, extended_attribute_list):
68    return any(extended_attribute in definition_or_member.extended_attributes
69               for extended_attribute in extended_attribute_list)
70
71
72def has_extended_attribute_value(definition_or_member, name, value):
73    extended_attributes = definition_or_member.extended_attributes
74    return (name in extended_attributes and
75            extended_attribute_value_contains(extended_attributes[name], value))
76
77
78def extended_attribute_value_as_list(definition_or_member, name):
79    extended_attributes = definition_or_member.extended_attributes
80    if name not in extended_attributes:
81        return None
82    value = extended_attributes[name]
83    if isinstance(value, list):
84        return value
85    return [value]
86
87
88################################################################################
89# String handling
90################################################################################
91
92def capitalize(name):
93    """Capitalize first letter or initial acronym (used in setter names)."""
94    for acronym in ACRONYMS:
95        if name.startswith(acronym.lower()):
96            return name.replace(acronym.lower(), acronym)
97    return name[0].upper() + name[1:]
98
99
100def strip_suffix(string, suffix):
101    if not suffix or not string.endswith(suffix):
102        return string
103    return string[:-len(suffix)]
104
105
106def uncapitalize(name):
107    """Uncapitalizes first letter or initial acronym (used in method names).
108
109    E.g., 'SetURL' becomes 'setURL', but 'URLFoo' becomes 'urlFoo'.
110    """
111    for acronym in ACRONYMS:
112        if name.startswith(acronym):
113            return name.replace(acronym, acronym.lower())
114    return name[0].lower() + name[1:]
115
116
117################################################################################
118# C++
119################################################################################
120
121def enum_validation_expression(idl_type):
122    # FIXME: Add IdlEnumType, move property to derived type, and remove this check
123    if not idl_type.is_enum:
124        return None
125    return ' || '.join(['string == "%s"' % enum_value
126                        for enum_value in idl_type.enum_values])
127IdlTypeBase.enum_validation_expression = property(enum_validation_expression)
128
129
130def scoped_name(interface, definition, base_name):
131    if 'ImplementedInPrivateScript' in definition.extended_attributes:
132        return '%s::PrivateScript::%s' % (v8_class_name(interface), base_name)
133    # partial interfaces are implemented as separate classes, with their members
134    # implemented as static member functions
135    partial_interface_implemented_as = definition.extended_attributes.get('PartialInterfaceImplementedAs')
136    if partial_interface_implemented_as:
137        return '%s::%s' % (partial_interface_implemented_as, base_name)
138    if (definition.is_static or
139        definition.name in ('Constructor', 'NamedConstructor')):
140        return '%s::%s' % (cpp_name(interface), base_name)
141    return 'impl->%s' % base_name
142
143
144def v8_class_name(interface):
145    return v8_types.v8_type(interface.name)
146
147
148################################################################################
149# Specific extended attributes
150################################################################################
151
152# [ActivityLogging]
153def activity_logging_world_list(member, access_type=''):
154    """Returns a set of world suffixes for which a definition member has activity logging, for specified access type.
155
156    access_type can be 'Getter' or 'Setter' if only checking getting or setting.
157    """
158    extended_attributes = member.extended_attributes
159    if 'LogActivity' not in extended_attributes:
160        return set()
161    log_activity = extended_attributes['LogActivity']
162    if log_activity and not log_activity.startswith(access_type):
163        return set()
164
165    includes.add('bindings/core/v8/V8DOMActivityLogger.h')
166    if 'LogAllWorlds' in extended_attributes:
167        return set(['', 'ForMainWorld'])
168    return set([''])  # At minimum, include isolated worlds.
169
170
171# [ActivityLogging]
172def activity_logging_world_check(member):
173    """Returns if an isolated world check is required when generating activity
174    logging code.
175
176    The check is required when there is no per-world binding code and logging is
177    required only for isolated world.
178    """
179    extended_attributes = member.extended_attributes
180    if 'LogActivity' not in extended_attributes:
181        return False
182    if ('PerWorldBindings' not in extended_attributes and
183        'LogAllWorlds' not in extended_attributes):
184        return True
185    return False
186
187
188# [CallWith]
189CALL_WITH_ARGUMENTS = {
190    'ScriptState': 'scriptState',
191    'ExecutionContext': 'executionContext',
192    'ScriptArguments': 'scriptArguments.release()',
193    'ActiveWindow': 'callingDOMWindow(info.GetIsolate())',
194    'FirstWindow': 'enteredDOMWindow(info.GetIsolate())',
195    'Document': 'document',
196}
197# List because key order matters, as we want arguments in deterministic order
198CALL_WITH_VALUES = [
199    'ScriptState',
200    'ExecutionContext',
201    'ScriptArguments',
202    'ActiveWindow',
203    'FirstWindow',
204    'Document',
205]
206
207
208def call_with_arguments(call_with_values):
209    if not call_with_values:
210        return []
211    return [CALL_WITH_ARGUMENTS[value]
212            for value in CALL_WITH_VALUES
213            if extended_attribute_value_contains(call_with_values, value)]
214
215
216# [Conditional]
217DELIMITER_TO_OPERATOR = {
218    '|': '||',
219    ',': '&&',
220}
221
222
223def conditional_string(definition_or_member):
224    extended_attributes = definition_or_member.extended_attributes
225    if 'Conditional' not in extended_attributes:
226        return None
227    return 'ENABLE(%s)' % extended_attributes['Conditional']
228
229
230# [DeprecateAs]
231def deprecate_as(member):
232    extended_attributes = member.extended_attributes
233    if 'DeprecateAs' not in extended_attributes:
234        return None
235    includes.add('core/frame/UseCounter.h')
236    return extended_attributes['DeprecateAs']
237
238
239# [Exposed]
240EXPOSED_EXECUTION_CONTEXT_METHOD = {
241    'DedicatedWorker': 'isDedicatedWorkerGlobalScope',
242    'ServiceWorker': 'isServiceWorkerGlobalScope',
243    'SharedWorker': 'isSharedWorkerGlobalScope',
244    'Window': 'isDocument',
245    'Worker': 'isWorkerGlobalScope',
246}
247
248
249def exposed(definition_or_member, interface):
250    exposure_set = extended_attribute_value_as_list(definition_or_member, 'Exposed')
251    if not exposure_set:
252        return None
253
254    interface_exposure_set = expanded_exposure_set_for_interface(interface)
255
256    # Methods must not be exposed to a broader scope than their interface.
257    if not set(exposure_set).issubset(interface_exposure_set):
258        raise ValueError('Interface members\' exposure sets must be a subset of the interface\'s.')
259
260    exposure_checks = []
261    for environment in exposure_set:
262        # Methods must be exposed on one of the scopes known to Blink.
263        if environment not in EXPOSED_EXECUTION_CONTEXT_METHOD:
264            raise ValueError('Values for the [Exposed] annotation must reflect to a valid exposure scope.')
265
266        exposure_checks.append('context->%s()' % EXPOSED_EXECUTION_CONTEXT_METHOD[environment])
267
268    return ' || '.join(exposure_checks)
269
270
271def expanded_exposure_set_for_interface(interface):
272    exposure_set = extended_attribute_value_as_list(interface, 'Exposed')
273
274    # "Worker" is an aggregation for the different kinds of workers.
275    if 'Worker' in exposure_set:
276        exposure_set.extend(('DedicatedWorker', 'SharedWorker', 'ServiceWorker'))
277
278    return sorted(set(exposure_set))
279
280
281# [GarbageCollected], [WillBeGarbageCollected]
282def gc_type(definition):
283    extended_attributes = definition.extended_attributes
284    if 'GarbageCollected' in extended_attributes:
285        return 'GarbageCollectedObject'
286    elif 'WillBeGarbageCollected' in extended_attributes:
287        return 'WillBeGarbageCollectedObject'
288    return 'RefCountedObject'
289
290
291# [ImplementedAs]
292def cpp_name(definition_or_member):
293    extended_attributes = definition_or_member.extended_attributes
294    if 'ImplementedAs' not in extended_attributes:
295        return definition_or_member.name
296    return extended_attributes['ImplementedAs']
297
298
299# [MeasureAs]
300def measure_as(definition_or_member):
301    extended_attributes = definition_or_member.extended_attributes
302    if 'MeasureAs' not in extended_attributes:
303        return None
304    includes.add('core/frame/UseCounter.h')
305    return extended_attributes['MeasureAs']
306
307
308# [PerContextEnabled]
309def per_context_enabled_function_name(definition_or_member):
310    extended_attributes = definition_or_member.extended_attributes
311    if 'PerContextEnabled' not in extended_attributes:
312        return None
313    feature_name = extended_attributes['PerContextEnabled']
314    return 'ContextFeatures::%sEnabled' % uncapitalize(feature_name)
315
316
317# [RuntimeEnabled]
318def runtime_enabled_function_name(definition_or_member):
319    """Returns the name of the RuntimeEnabledFeatures function.
320
321    The returned function checks if a method/attribute is enabled.
322    Given extended attribute RuntimeEnabled=FeatureName, return:
323        RuntimeEnabledFeatures::{featureName}Enabled
324    """
325    extended_attributes = definition_or_member.extended_attributes
326    if 'RuntimeEnabled' not in extended_attributes:
327        return None
328    feature_name = extended_attributes['RuntimeEnabled']
329    return 'RuntimeEnabledFeatures::%sEnabled' % uncapitalize(feature_name)
330