File: codegen_context.py

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (354 lines) | stat: -rw-r--r-- 13,003 bytes parent folder | download | duplicates (6)
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
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import copy

import web_idl

from .codegen_format import NonRenderable


class CodeGenContext(object):
    """
    Represents a context of code generation.

    Note that this is not Mako template context itself, however
    CodeGenContext's attributes will be bound as Mako's global template
    variables as below.

      code_node.set_base_template_vars(cg_context.template_bindings())

    Then, |CodeGenContext.interface| will be available in template text as
    "${interface}".  So, an instance of CodeGenContext represents a set of
    global template variables.

    CodeGenContext is immutable.  A new state should be created via
    |make_copy|.

      new_cg_context = old_cg_context.make_copy(
          var1=new_value1, var2=new_value2, ...)

    The immutability is important because CodeNodes may be created lazily from
    an instance of CodeGenContext.  For example,

      def foo(cg_context):
        def define_symbol(symbol_node):
          node = SymbolDefinitionNode(symbol_node)
          node.append(TextNode("{}".format(cg_context.class_name)))
          return node
        symbol = SymbolNode("sym", definition_constructor=define_symbol)
        ...

    in this case, |define_symbol| may be run after the execution of |foo|
    completes.  |define_symbol| is a closure which captures |cg_context|.
    So, it's important that CodeGenContext is immutable in order to avoid any
    surprising side effect.
    """

    # "for_world" attribute values
    MAIN_WORLD = "main"
    NON_MAIN_WORLDS = "other"
    ALL_WORLDS = "all"

    # "v8_callback_type" attribute values
    #
    # void (*)(const v8::FunctionCallbackInfo<v8::Value>&)
    V8_FUNCTION_CALLBACK = "v8::FunctionCallback"
    # void (*)(v8::Local<v8::Name>,
    #          const v8::PropertyCallbackInfo<v8::Value>&)
    V8_ACCESSOR_NAME_GETTER_CALLBACK = "v8::AccessorNameGetterCallback"
    # void (*)(v8::Local<v8::Name>, v8::Local<v8::Value>,
    #          const v8::PropertyCallbackInfo<void>&)
    V8_ACCESSOR_NAME_SETTER_CALLBACK = "v8::AccessorNameSetterCallback"
    # v8::Intercepted (*)(v8::Local<v8::Name>,
    #                     const v8::PropertyCallbackInfo<v8::Value>&)
    V8_NAMED_PROPERTY_GETTER_CALLBACK = "v8::NamedPropertyGetterCallback"
    # v8::Intercepted (*)(v8::Local<v8::Name>, v8::Local<v8::Value>,
    #                     const v8::PropertyCallbackInfo<void>&)
    V8_NAMED_PROPERTY_SETTER_CALLBACK = "v8::NamedPropertySetterCallback"
    # Others
    V8_OTHER_CALLBACK = "other callback type"

    @classmethod
    def init(cls):
        """Initialize the class.  Must be called exactly once."""
        assert not hasattr(cls, "_was_initialized"), "Do not call twice."
        cls._was_initialized = True

        # List of
        #   attribute name: default value
        cls._context_attrs = {
            # Top-level definition
            "async_iterator": None,
            "callback_function": None,
            "callback_interface": None,
            "dictionary": None,
            "enumeration": None,
            "interface": None,
            "namespace": None,
            "observable_array": None,
            "sync_iterator": None,
            "typedef": None,
            "union": None,

            # Class-member-ish definition
            "attribute": None,
            "attribute_get": False,
            "attribute_set": False,
            "constant": None,
            "constructor": None,
            "constructor_group": None,
            "dict_member": None,
            "exposed_construct": None,
            "is_legacy_factory_function": False,
            "legacy_window_alias": None,
            "operation": None,
            "operation_group": None,

            # Special member-ish definition
            "indexed_interceptor_kind": None,
            "indexed_property_getter": None,
            "indexed_property_setter": None,
            "named_interceptor_kind": None,
            "named_property_getter": None,
            "named_property_setter": None,
            "named_property_deleter": None,
            "stringifier": None,

            # Cache of a tuple of dictionary._DictionaryMember for the own
            # members of the dictionary of which the Blink class is being
            # generated.  The cache is used in dictionary.py to save code
            # generation time.
            "dictionary_own_members": (),
            # Cache of a tuple of union._UnionMember for the flattened member
            # types of the union of which the Blink class is being generated.
            # The cache is used in union.py to save code generation time.
            "union_members": (),

            # The names of the class being generated and its base class.
            "base_class_name": None,
            "class_name": None,

            # Main world or all worlds
            # Used via [PerWorldBindings] to optimize the code path of the main
            # world.
            "for_world": cls.ALL_WORLDS,

            # True when generating a callback of [NoAllocDirectCall].
            "no_alloc_direct_call": False,

            # Type of V8 callback function which implements IDL attribute,
            # IDL operation, etc.
            "v8_callback_type": cls.V8_FUNCTION_CALLBACK,
        }

        # List of computational attribute names
        cls._computational_attrs = (
            "class_like",
            "function_like",
            "idl_definition",
            "idl_location",
            "idl_location_and_name",
            "idl_name",
            "may_throw_exception",
            "member_like",
            "property_",
            "return_type",
        )

        # Define public readonly properties of this class.
        for attr in cls._context_attrs.keys():

            def make_get():
                _attr = cls._internal_attr(attr)

                def get(self):
                    return getattr(self, _attr)

                return get

            setattr(cls, attr, property(make_get()))

    @staticmethod
    def _internal_attr(attr):
        return "_{}".format(attr)

    def __init__(self, **kwargs):
        assert CodeGenContext._was_initialized

        for arg in kwargs.keys():
            assert arg in self._context_attrs, "Unknown argument: {}".format(
                arg)

        for attr, default_value in self._context_attrs.items():
            value = kwargs[attr] if attr in kwargs else default_value
            assert (default_value is None
                    or type(value) is type(default_value)), (
                        "Type mismatch at argument: {}".format(attr))
            setattr(self, self._internal_attr(attr), value)

    def make_copy(self, **kwargs):
        """
        Returns a copy of this context applying the updates given as the
        arguments.
        """
        for arg in kwargs.keys():
            assert arg in self._context_attrs, "Unknown argument: {}".format(
                arg)

        new_object = copy.copy(self)

        for attr, new_value in kwargs.items():
            old_value = getattr(self, attr)
            assert old_value is None or type(new_value) is type(old_value), (
                "Type mismatch at argument: {}".format(attr))
            setattr(new_object, self._internal_attr(attr), new_value)

        return new_object

    def template_bindings(self):
        """
        Returns a bindings object to be passed into
        |CodeNode.add_template_vars|.  Only properties with a non-None value are
        bound so that it's easy to detect invalid use cases (use of an unbound
        template variable raises a NameError).
        """
        bindings = {}

        for attr in self._context_attrs.keys():
            value = getattr(self, attr)
            if value is None:
                value = NonRenderable(attr)
            bindings[attr] = value

        for attr in self._computational_attrs:
            value = getattr(self, attr)
            if value is None:
                value = NonRenderable(attr)
            bindings[attr.strip("_")] = value

        return bindings

    @property
    def class_like(self):
        return (self.async_iterator or self.callback_interface
                or self.dictionary or self.interface or self.namespace
                or self.sync_iterator)

    @property
    def does_override_idl_return_type(self):
        # Blink implementation returns in a type different from the IDL type.
        # Namely, IndexedPropertySetterResult, NamedPropertySetterResult, and
        # NamedPropertyDeleterResult are returned ignoring the operation's
        # return type.
        return (self.indexed_property_setter or self.named_property_setter
                or self.named_property_deleter)

    @property
    def function_like(self):
        return (self.callback_function or self.constructor or self.operation
                or self._indexed_or_named_property)

    @property
    def idl_definition(self):
        return (self.callback_function or self.callback_interface
                or self.dictionary or self.enumeration or self.interface
                or self.namespace or self.typedef or self.union)

    @property
    def idl_location(self):
        idl_def = self.member_like or self.idl_definition
        if idl_def and not isinstance(idl_def, web_idl.Union):
            location = idl_def.debug_info.location
            text = location.filepath
            if location.line_number is not None:
                text += ":{}".format(location.line_number)
            return text
        return "<<unknown path>>"

    @property
    def idl_location_and_name(self):
        return "{}: {}".format(self.idl_location, self.idl_name)

    @property
    def idl_name(self):
        member = self.member_like or self.property_
        if member:
            return "{}.{}".format(self.class_like.identifier,
                                  member.identifier)
        if self.idl_definition:
            return self.idl_definition.identifier
        return "<<unknown name>>"

    @property
    def is_return_type_promise_type(self):
        if self.attribute:
            return self.attribute.idl_type.unwrap().is_promise
        if self.operation_group:
            return self.operation_group[0].return_type.unwrap().is_promise
        return False

    @property
    def is_interceptor_returning_v8intercepted(self):
        return bool((self.indexed_interceptor_kind
                     and self.indexed_interceptor_kind != "Enumerator")
                    or (self.named_interceptor_kind
                        and self.named_interceptor_kind != "Enumerator")
                    or (self.v8_callback_type
                        == CodeGenContext.V8_NAMED_PROPERTY_GETTER_CALLBACK)
                    or (self.v8_callback_type
                        == CodeGenContext.V8_NAMED_PROPERTY_SETTER_CALLBACK))

    @property
    def logging_target(self):
        return (self.attribute or self.constant or self.constructor
                or self.constructor_group or self.dict_member
                or (self.legacy_window_alias or self.exposed_construct)
                or self.operation or self.operation_group
                or (self.stringifier and self.stringifier.operation)
                or self._indexed_or_named_property)

    @property
    def may_throw_exception(self):
        if not self.member_like:
            return False
        ext_attr = self.member_like.extended_attributes.get("RaisesException")
        if not ext_attr:
            return False
        return (not ext_attr.values
                or (self.attribute_get and "Getter" in ext_attr.values)
                or (self.attribute_set and "Setter" in ext_attr.values))

    @property
    def member_like(self):
        return (self.attribute or self.constant or self.constructor
                or self.dict_member or self.operation
                or self._indexed_or_named_property)

    @property
    def property_(self):
        return (self.attribute or self.constant or self.constructor_group
                or self.dict_member
                or (self.legacy_window_alias or self.exposed_construct)
                or self.operation_group
                or (self.stringifier and self.stringifier.operation)
                or self._indexed_or_named_property)

    @property
    def return_type(self):
        if self.attribute_get:
            return self.attribute.idl_type
        function_like = self.function_like
        if function_like:
            return function_like.return_type
        return None

    @property
    def _indexed_or_named_property(self):
        return (self.indexed_property_getter or self.indexed_property_setter
                or self.named_property_getter or self.named_property_setter
                or self.named_property_deleter)


CodeGenContext.init()