blob: ae1a3ac3c964839c21f432599828f98e00f0f7c7 [file] [log] [blame]
# Copyright 2019 The Chromium Authors. All rights reserved.
# 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 . import name_style
from .codegen_format import NonRenderable
from .path_manager import PathManager
class CodeGenContext(object):
"""
Represents a context of code generation.
Note that this is not relevant to Mako template context or any contexts.
Also note that CodeGenContext's attributes will be global template
variables. |CodeGenContext.interface| will be available in templates as
"${interface}".
"""
# "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"
# void (*)(v8::Local<v8::Name>, v8::Local<v8::Value>,
# const v8::PropertyCallbackInfo<v8::Value>&)
V8_GENERIC_NAMED_PROPERTY_SETTER_CALLBACK = (
"v8::GenericNamedPropertySetterCallback")
# 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
"callback_function": None,
"callback_interface": None,
"dictionary": None,
"enumeration": None,
"interface": None,
"namespace": 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_named_constructor": False,
"legacy_window_alias": None,
"operation": None,
"operation_group": None,
# Special member-ish definition
"indexed_property_getter": None,
"indexed_property_setter": None,
"named_property_getter": None,
"named_property_setter": None,
"named_property_deleter": None,
"stringifier": None,
# Contents of union.
"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,
# 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",
"is_return_by_argument",
"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.callback_interface or self.dictionary or self.interface
or self.namespace)
@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_by_argument(self):
if self.does_override_idl_return_type:
return False
if self.return_type is None:
return False
return self.return_type.unwrap().is_union
@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 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()