Commit 6bf89d30 authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

Reland "bind-gen: Support generation of function definitions."

This is a reland of b3a546fe

It turned out that it's not going well to output v8_example.cc
under bindings/scripts/bind_gen/, so reverted the path to
out/Default/gen/... as same as before.

Added some asserts in example.py.

Original change's description:
> bind-gen: Support generation of function definitions.
>
> This is just a first step to support the code generation
> of bindings callback functions.
>
> Introduces CodeGenerationContext for convenience, and
> also demonstrates make_common_local_vars().
>
> Bug: 839389
> Change-Id: I9ff49b4bfc6bd98e6b29687cc01f986c2baa01fa
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1855325
> Reviewed-by: Hitoshi Yoshida <peria@chromium.org>
> Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#709389}

Bug: 839389
Change-Id: I57ff27abdf2a01a9cc926bbf27d1a0c8ceb5ce88
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1880991
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709442}
parent f514a8b3
# 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
class CodeGenerationContext(object):
"""
Represents a context of code generation.
Note that this is not relevant to Mako template context or any contexts.
Also note that CodeGenerationContext's attributes will be global template
variables. |CodeGenerationContext.interface| will be available in templates
as "${interface}".
"""
# "world" attribute values
MAIN_WORLD = "main"
ALL_WORLDS = "all"
@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_interface": None,
"dictionary": None,
"interface": None,
"namespace": None,
# Class-member-ish definition
"attribute": None,
"attribute_get": False,
"attribute_set": False,
"constant": None,
"constructor": None,
"constructor_group": None,
"dict_member": None,
"operation": None,
"operation_group": None,
# Main world or all worlds
"world": cls.ALL_WORLDS,
}
# List of computational attribute names
cls._computational_attrs = (
"class_like",
"member_like",
"property",
)
# Define public readonly properties of this class.
for attr in cls._context_attrs.iterkeys():
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 CodeGenerationContext._was_initialized
for arg in kwargs.iterkeys():
assert arg in self._context_attrs, "Unknown argument: {}".format(
arg)
for attr, default_value in self._context_attrs.iteritems():
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.iterkeys():
assert arg in self._context_attrs, "Unknown argument: {}".format(
arg)
new_object = copy.copy(self)
for attr, new_value in kwargs.iteritems():
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.iterkeys():
value = getattr(self, attr)
if value is not None:
bindings[attr] = value
for attr in self._computational_attrs:
value = getattr(self, attr)
if value is not None:
bindings[attr] = value
return bindings
@property
def class_like(self):
return (self.callback_interface or self.dictionary or self.interface
or self.namespace)
@property
def member_like(self):
return (self.attribute or self.constant or self.constructor
or self.dict_member or self.operation)
@property
def property(self):
return (self.attribute or self.constant or self.constructor_group
or self.dict_member or self.operation_group)
CodeGenerationContext.init()
......@@ -479,10 +479,11 @@ class SymbolNode(CodeNode):
self._definition_node_constructor = definition_node_constructor
def _render(self, renderer, last_render_state):
if not renderer.last_caller.is_code_symbol_defined(self):
for caller in renderer.callers:
assert isinstance(caller, CodeNode)
caller.on_undefined_code_symbol_found(self)
for caller in renderer.callers:
assert isinstance(caller, CodeNode)
if caller.is_code_symbol_defined(self):
break
caller.on_undefined_code_symbol_found(self)
return self.name
......
......@@ -4,28 +4,133 @@
import os.path
from . import code_node
from .clang_format import clang_format
from .code_generation_context import CodeGenerationContext
from .code_node import CodeNode
from .code_node import FunctionDefinitionNode
from .code_node import LiteralNode
from .code_node import SymbolNode
from .code_node import SymbolScopeNode
from .code_node import TextNode
from .mako_renderer import MakoRenderer
def make_common_local_vars(cg_context):
assert isinstance(cg_context, CodeGenerationContext)
S = SymbolNode
T = TextNode
local_vars = []
supplemental_bindings = {}
local_vars.extend([
S("class_like_name", ("const char* const ${class_like_name} = "
"\"${class_like.identifier}\";")),
S("current_context", ("v8::Local<v8::Context> ${current_context} = "
"${isolate}->GetCurrentContext();")),
S("current_script_state", ("ScriptState* ${current_script_state} = "
"ScriptState::ForCurrentRealm(${info});")),
S("exception_state",
("ExceptionState ${exception_state}("
"${isolate}, ${exception_state_context_type}, ${class_like_name}, "
"${property_name});${reject_promise_scope_definition}")),
S("isolate", "v8::Isolate* ${isolate} = ${info}.GetIsolate();"),
S("per_context_data", ("V8PerContextData* ${per_context_data} = "
"${script_state}->PerContextData();")),
S("per_isolate_data", ("V8PerIsolateData* ${per_isolate_data} = "
"V8PerIsolateData::From(${isolate});")),
S("property_name",
"const char* const ${property_name} = \"${property.identifier}\";"),
S("receiver", "v8::Local<v8::Object> ${receiver} = ${info}.This();"),
S("receiver_context", ("v8::Local<v8::Context> ${receiver_context} = "
"${receiver_script_state}->GetContext();")),
S("receiver_script_state",
("ScriptState* ${receiver_script_state} = "
"ScriptState::ForRelevantRealm(${info});")),
])
# script_state
def_text = "ScriptState* ${script_state} = {_1};"
if cg_context.member_like and not cg_context.member_like.is_static:
_1 = "${receiver_script_state}"
else:
_1 = "${current_script_state}"
local_vars.append(
S("script_state", CodeNode.format_template(def_text, _1=_1)))
# exception_state_context_type
def_text = (
"const ExceptionState::ContextType ${exception_state_context_type} = "
"{_1};")
if cg_context.attribute_get:
_1 = "ExceptionState::kGetterContext"
elif cg_context.attribute_set:
_1 = "ExceptionState::kSetterContext"
elif cg_context.constructor:
_1 = "ExceptionState::kConstructionContext"
else:
_1 = "ExceptionState::kExecutionContext"
local_vars.append(
S("exception_state_context_type",
CodeNode.format_template(def_text, _1=_1)))
# reject_promise_scope_definition
if ((cg_context.attribute_get and cg_context.attribute.idl_type.is_promise)
or (cg_context.operation
and cg_context.operation.return_type.is_promise)):
template_text = ("\n"
"ExceptionToRejectPromiseScope reject_promise_scope"
"(${info}, ${exception_state});")
else:
template_text = ""
supplemental_bindings["reject_promise_scope_definition"] = T(template_text)
return local_vars, supplemental_bindings
def make_attribute_get_def(cg_context):
assert isinstance(cg_context, CodeGenerationContext)
L = LiteralNode
T = TextNode
cg_context = cg_context.make_copy(attribute_get=True)
local_vars, local_vars_bindings = make_common_local_vars(cg_context)
func_def = FunctionDefinitionNode(
name=L("AttributeGetCallback"),
arg_decls=[L("const v8::FunctionCallbackInfo<v8::Value>& info")],
return_type=L("void"),
local_vars=local_vars)
body = func_def.body
body.add_template_var("info", "info")
body.add_template_vars(local_vars_bindings)
body.add_template_vars(cg_context.template_bindings())
body.extend([
T("(void)(${per_context_data}, ${exception_state});"),
])
return func_def
def run_example(web_idl_database, output_dirs):
renderer = MakoRenderer()
filename = 'v8_example.cc'
filepath = os.path.join(output_dirs['core'], filename)
root_node = code_node.SymbolScopeNode(renderer=renderer)
root_node.extend([
code_node.TextNode("${z} = ${x} + ${y};"),
code_node.TextNode("print ${z};"),
])
namespace = list(web_idl_database.namespaces)[0]
attribute = namespace.attributes[0]
root_node.register_code_symbols([
code_node.SymbolNode('x', "int ${x} = 1;"),
code_node.SymbolNode('y', "int ${y} = 2;"),
code_node.SymbolNode('z', "int ${z};"),
])
cg_context = CodeGenerationContext(
namespace=namespace, attribute=attribute)
root_node = SymbolScopeNode(renderer=renderer)
root_node.append(make_attribute_get_def(cg_context))
prev = ''
current = str(root_node)
......
......@@ -21,6 +21,7 @@
../../build/scripts/blinkbuild/name_style_converter.py
bind_gen/__init__.py
bind_gen/clang_format.py
bind_gen/code_generation_context.py
bind_gen/code_node.py
bind_gen/example.py
bind_gen/mako_renderer.py
......
......@@ -12,7 +12,7 @@
};
namespace TestNamespace {
readonly attribute boolean attr1;
readonly attribute Promise<boolean> attr1;
readonly attribute DOMString attr2;
void op1();
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment