blob: 30f7fd82b331a06f4c4e0cb4e3b89f75ffe6b181 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 Apple 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:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. 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.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
# This script generates the source files for WebGL GPU process IPC.
#
# python3 -m black -l 150 Tools/Scripts/generate-gpup-webgl
# python3 -m mypy Tools/Scripts/generate-gpup-webgl
#
import argparse
import enum
import pathlib
import re
import sys
import collections
from typing import List, Dict, Iterable, Callable, Tuple, Set, Optional, Generator, Iterator, Counter
root_dir = (pathlib.Path(__file__).parent / ".." / "..").resolve()
extensions_input_fn = root_dir / "Source" / "WebCore" / "platform" / "graphics" / "ExtensionsGL.h"
functions_input_fns = [
root_dir / "Source" / "WebKit" / "WebProcess" / "GPU" / "graphics" / "RemoteGraphicsContextGLProxy.h"
]
types_input_fn = root_dir / "Source" / "WebKit" / "WebProcess" / "GPU" / "graphics" / "RemoteGraphicsContextGLProxy.h"
template_preamble = """/* Copyright (C) 2020 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
*/
// This file is generated by generate-gpup-webgl. Do not edit.
"""
context_functions_fn = root_dir / "Source" / "WebKit" / "GPUProcess" / "graphics" / "RemoteGraphicsContextGLFunctionsGenerated.h"
context_functions_template = (
template_preamble
+ """// This file should be included in the private section of the
// RemoteGraphicsContextGL implementations.
#pragma once
{}
"""
)
context_messages_fn = root_dir / "Source" / "WebKit" / "GPUProcess" / "graphics" / "RemoteGraphicsContextGL.messages.in"
context_messages_template = (
template_preamble
+ """#if ENABLE(GPU_PROCESS) && ENABLE(WEBGL)
messages -> RemoteGraphicsContextGL NotRefCounted Stream {{
void Reshape(int32_t width, int32_t height)
#if PLATFORM(COCOA)
void PrepareForDisplay() -> (MachSendRight displayBuffer) Synchronous NotStreamEncodableReply
#endif
#if !PLATFORM(COCOA)
void PrepareForDisplay() -> () Synchronous
#endif
void EnsureExtensionEnabled(String extension)
void NotifyMarkContextChanged()
void SynthesizeGLError(uint32_t error)
void GetError() -> (uint32_t returnValue) Synchronous
void PaintRenderingResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer) -> () Synchronous
void PaintCompositedResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer) -> () Synchronous
void CopyTextureFromMedia(WebCore::MediaPlayerIdentifier identifier, uint32_t texture, uint32_t target, int32_t level, uint32_t internalFormat, uint32_t format, uint32_t type, bool premultiplyAlpha, bool flipY) -> (bool success) Synchronous
void SimulateEventForTesting(WebCore::GraphicsContextGL::SimulatedEventForTesting event)
{}
}}
#endif
"""
)
context_proxy_functions_fn = root_dir / "Source" / "WebKit" / "WebProcess" / "GPU" / "graphics" / "RemoteGraphicsContextGLProxyFunctionsGenerated.cpp"
context_proxy_functions_template = (
template_preamble
+ """
#include "config.h"
#include "RemoteGraphicsContextGLProxy.h"
#if ENABLE(GPU_PROCESS) && ENABLE(WEBGL)
namespace WebKit {{
{}
}}
#endif
"""
)
def write_file(fn, new_contents):
try:
with open(fn) as f:
if f.read() == new_contents:
return
except:
pass
with open(fn, "w") as f:
f.write(new_contents)
class cpp_type(object):
type_name: str
def __init__(self, type_name : str):
self.type_name = type_name
def __str__(self):
return self.type_name
def __hash__(self):
return hash(self.type_name)
def __repr__(self):
return self.type_name
def __eq__(self, other):
return self.type_name == other.type_name
def is_void(self) -> bool:
return self.type_name in ["void", "GCGLvoid", "const void", "const GCGLvoid"]
def is_const(self) -> bool:
return self.type_name.startswith("const ")
def is_container(self) -> bool:
return False
def is_output_buffer_type(self) -> bool:
return False
def is_span(self) -> bool:
return False
def is_dynamic_span(self) -> bool:
return False
def is_pointer(self) -> bool:
return False
def is_const_pointer(self) -> bool:
return False
def is_reference(self) -> bool:
return False
def is_const_reference(self) -> bool:
return False
def get_value_type(self) -> 'cpp_type':
return self
def get_decay_type(self) -> 'cpp_type':
if self.is_const():
return get_cpp_type(self.type_name[6:])
return self
def get_rvalue_type(self) -> 'cpp_type':
return get_cpp_type(self.type_name + "&&")
def get_pointer_type(self) -> 'cpp_type':
return get_cpp_type(self.type_name + "*")
class cpp_type_container(cpp_type):
def __init__(self, type_name : str, container_name : str, contained_type : cpp_type, arity : Optional[int] = None):
cpp_type.__init__(self, type_name)
self.container_name = container_name
self.contained_type = contained_type
self.arity = arity
def is_container(self) -> bool:
return True
def is_span(self) -> bool:
return self.container_name == "GCGLSpan"
def is_dynamic_span(self) -> bool:
return self.container_name == "GCGLSpan" and self.arity is None
def get_container_name(self):
return self.container_name
def get_arity(self) -> Optional[int]:
return self.arity
def get_contained_type(self) -> cpp_type:
return self.contained_type
def is_output_buffer_type(self) -> bool:
return self.contained_type.is_const()
def create_cpp_type_container(type_name : str) -> Optional[cpp_type_container]:
# The logic to determine container is to just assume all templates are containers.
m = re.match(r"([\w:]+)<(.+)>$", type_name)
if not m:
return None
container_name = m[1]
# All templates are containers except these below.
if container_name in ['RefPtr', 'Ref']:
return None
templates = m[2]
arity = None
m = re.match(r"([^,]+),\s*(\d+)", templates)
if m:
templates = m[1]
arity = int(m[2])
return cpp_type_container(type_name, container_name, get_cpp_type(templates), arity)
class cpp_type_function(cpp_type):
def __init__(self, type_name : str, return_value_type : cpp_type, argument_types : List[cpp_type]):
cpp_type.__init__(self, type_name)
self.return_value_type = return_value_type
self.argument_types = argument_types
def cpp_split_args_specs(args_spec : str) -> Iterator[str]:
# https://stackoverflow.com/questions/33527245/python-split-by-comma-skipping-the-content-inside-parentheses
comma = r",(?!(?:[^<]*\<[^>]*\>)*[^<>]*\>)"
return filter(None, [a.strip() for a in re.split(comma, args_spec.strip())])
def create_cpp_type_function(type_name : str) -> Optional[cpp_type_function]:
m = re.match(r'(.*)\((.*)\)', type_name)
if not m:
return None
return_value_type = get_cpp_type(m[1])
args_types = [get_cpp_type(a) for a in cpp_split_args_specs(m[2])]
return cpp_type_function(type_name, return_value_type, args_types)
# Pointer or refererence
class cpp_type_indirect(cpp_type):
category : str
value_type : cpp_type
def __init__(self, type_name : str, category : str, value_type : cpp_type):
cpp_type.__init__(self, type_name)
self.category = category
self.value_type = value_type
def is_const_pointer(self):
return self.category == '*' and self.is_const()
def is_pointer(self):
return self.category == '*' and not self.is_const()
def is_const_reference(self):
return self.category == '&' and self.is_const()
def is_reference(self):
return self.category == '&' and not self.is_const()
def is_rvalue_reference(self):
return self.category == '&&'
def get_value_type(self):
return self.value_type
def create_cpp_type_indirect(type_name : str) -> Optional[cpp_type_indirect]:
m = re.match(r"const (.+)&&$", type_name)
if not m:
m = re.match(r"(.+)&&$", type_name)
if m:
return cpp_type_indirect(type_name, '&&', get_cpp_type(m[1]))
m = re.match(r"const (.+)&$", type_name)
if not m:
m = re.match(r"(.+)&$", type_name)
if m:
return cpp_type_indirect(type_name, '&', get_cpp_type(m[1]))
m = re.match(r"const (.+)\*$", type_name)
if not m:
m = re.match(r"(.+)\*$", type_name)
if m:
return cpp_type_indirect(type_name, '*', get_cpp_type(m[1]))
return None
class cpp_expr(object):
type: cpp_type
expr: str
def __init__(self, type: cpp_type, expr: str):
self.type = type
self.expr = expr
def __str__(self):
return self.expr
CppExprConverter = Callable[[cpp_expr, cpp_type], cpp_expr]
def cpp_reinterpret_cast_from_pointer_through(cast_through_type : cpp_type) -> Callable[[cpp_expr, cpp_type], cpp_expr]:
def cpp_reinterpret_cast_from_pointer(expr: cpp_expr, type: cpp_type) -> cpp_expr:
return cpp_expr(type, f"static_cast<{type.type_name}>(reinterpret_cast<{str(cast_through_type)}>({str(expr)}))")
return cpp_reinterpret_cast_from_pointer
def cpp_reinterpret_cast_to_pointer_through(cast_through_type : cpp_type) -> Callable[[cpp_expr, cpp_type], cpp_expr]:
def cpp_reinterpret_cast_to_pointer(expr: cpp_expr, type: cpp_type) -> cpp_expr:
return cpp_expr(type, f"reinterpret_cast<{type.type_name}>(static_cast<{str(cast_through_type)}>({str(expr)}))")
return cpp_reinterpret_cast_to_pointer
def cpp_static_cast(expr: cpp_expr, type: cpp_type) -> cpp_expr:
return cpp_expr(type, f"static_cast<{type.type_name}>({str(expr)})")
def cpp_implicit_cast(expr: cpp_expr, type: cpp_type) -> cpp_expr:
return cpp_expr(type, str(expr))
def cpp_array_reinterpret_cast_conversion(expr: cpp_expr, type: cpp_type_container) -> cpp_expr:
element_pointer_type = type.get_contained_type().get_pointer_type()
return cpp_expr(type, f"{str(type)}(reinterpret_cast<{element_pointer_type}>({str(expr)}.data()), {str(expr)}.size())")
def webkit_ipc_span_transfer_type_reinterpret_cast_conversion(expr: cpp_expr, type: cpp_type_container) -> cpp_expr:
element_pointer_type = type.get_contained_type().get_pointer_type()
return cpp_expr(type, f"{str(type)}(reinterpret_cast<const {element_pointer_type}>({str(expr)}.data), {str(expr)}.bufSize)")
def cpp_move_expr(expr: cpp_expr) -> cpp_expr:
return cpp_expr(expr.type.get_rvalue_type(), f"WTFMove({str(expr)})")
cpp_types: Dict[str, cpp_type] = {}
cpp_type_constructors : List[Callable[[str], Optional[cpp_type]]] = [
create_cpp_type_function,
create_cpp_type_container,
create_cpp_type_indirect
]
def get_cpp_type(type_name: str):
r = cpp_types.get(type_name, None)
if r:
return r
for type_constr in cpp_type_constructors:
r = type_constr(type_name)
if r:
break
if not r:
r = cpp_type(type_name)
cpp_types[type_name] = r
return r
class cpp_arg(object):
name: str
type: cpp_type
def __init__(self, type: cpp_type, name: str):
self.name = name
self.type = type
def __str__(self):
return "{} {}".format(self.type, self.name)
class cpp_decl(object):
type: cpp_type
name: str
def __init__(self, type: cpp_type, name: str):
self.type = type
self.name = name
def __str__(self):
return f"{str(self.type)} {self.name}"
class cpp_arg_list(object):
args: List[cpp_arg]
def __init__(self, args: List[cpp_arg]):
self.args = args
def names(self):
return ", ".join(a.name for a in self.args)
def exprs(self):
return [cpp_expr(a.type, a.name) for a in self.args]
def decls(self):
return [cpp_decl(a.type, a.name) for a in self.args]
def types(self):
return [a.type for a in self.args]
def __str__(self):
return ", ".join(str(a) for a in self.args)
class cpp_func(object):
name: str
args: cpp_arg_list
return_type: cpp_type
is_extension: bool
overload_suffix: str
def __init__(self, name: str, return_type: cpp_type, args: cpp_arg_list, is_extension: bool):
self.name = name
self.return_type = return_type
self.args = args
self.is_extension = is_extension
self.overload_suffix = ""
def __str__(self):
return f"{self.return_type} {self.name}({str(self.args)})"
def get_args_categories(self) -> Tuple[List[cpp_arg], List[cpp_arg]]:
in_args = []
out_args = []
for a in self.args.args:
#fmt: off
if a.type.is_pointer() or \
a.type.is_reference():
out_args += [a]
elif isinstance(a.type, cpp_type_container) and (((a.type.is_span() or a.type.is_dynamic_span()) and not a.type.get_contained_type().is_const())):
out_args += [a]
else:
in_args += [a]
#fmt: on
return in_args, out_args
def is_implemented_type(self, type: cpp_type):
if type.is_const_pointer():
return False
return True
def is_implemented(self):
#" in a.type.type_name for a in self.args.args):
# return False
#if "GCGLsync" in self.return_type.type_name:
# return False
if any(a.name == "bufSize" for a in self.args.args):
return False
if any(a.type.is_pointer() and a.type.get_value_type().is_void() for a in self.args.args):
return False
if any(not self.is_implemented_type(a.type) for a in self.args.args):
return False
if self.return_type.is_pointer():
return False
if not self.is_implemented_type(self.return_type):
return False
return True
webkit_ipc_types: Dict[cpp_type, cpp_type] = {}
webkit_ipc_types_converters: Dict[Tuple[cpp_type, cpp_type], CppExprConverter] = {}
def webkit_ipc_convert_expr(expr: cpp_expr, type: cpp_type) -> cpp_expr:
""" Converts `expr` of type of `self` to `type` """
convert = webkit_ipc_types_converters.get((expr.type, type), None)
if not convert:
if expr.type == type:
return expr
convert = cpp_implicit_cast
return convert(expr, type)
def webkit_ipc_get_span_transfer_type(type : cpp_type_container) -> cpp_type:
element_type = type.get_contained_type().get_decay_type()
arity = type.get_arity()
webkit_ipc_element_type = webkit_ipc_types[element_type] if not element_type.is_void() else get_cpp_type('uint8_t')
if arity is not None:
return get_cpp_type(f"IPC::ArrayReference<{str(webkit_ipc_element_type)}, {arity}>")
return get_cpp_type(f"IPC::ArrayReference<{str(webkit_ipc_element_type)}>")
def webkit_ipc_get_span_store_type(type: cpp_type_container) -> cpp_type:
element_type = type.get_contained_type().get_decay_type()
if element_type.is_void():
element_type = get_cpp_type('GCGLchar')
if type.is_dynamic_span():
inline_capacity = 16 if "float" in element_type.type_name else 4
return get_cpp_type(f"Vector<{str(element_type)}, {str(inline_capacity)}>")
return get_cpp_type(f"std::array<{str(element_type)}, {str(type.get_arity())}>")
def webkit_ipc_make_gcglspan_conversion(expr : cpp_expr, type : cpp_type_container) -> cpp_expr:
pointer_cast_type = type.get_contained_type().get_pointer_type()
if type.is_dynamic_span():
return cpp_expr(type, f"makeGCGLSpan(reinterpret_cast<{str(pointer_cast_type)}>({str(expr)}.data()), {str(expr)}.size())")
assert(type.is_span())
return cpp_expr(type, f"makeGCGLSpan<{str(type.get_arity())}>(reinterpret_cast<{str(pointer_cast_type)}>({str(expr)}.data()))")
# See messages.py function_parameter_type
webkit_ipc_builtin_types = set(
["uint8_t", "uint16_t", "uint32_t", "uint64_t", "int8_t", "int16_t", "int32_t", "int64_t", "bool", "float", "double" "bool"]
)
def webkit_ipc_get_message_forwarder_type(type: cpp_type) -> cpp_type:
if type.type_name in webkit_ipc_builtin_types:
return type
return type.get_rvalue_type()
def webkit_ipc_move_expr(expr: cpp_expr) -> cpp_expr:
if expr.type.type_name in webkit_ipc_builtin_types:
return expr
return cpp_move_expr(expr)
def webkit_ipc_msg_name(func: cpp_func):
return func.name[0].capitalize() + func.name[1:] + func.overload_suffix
class webkit_ipc_msg(object):
name: str
in_args: cpp_arg_list
out_args: cpp_arg_list
def __init__(self, func: cpp_func):
self.name = webkit_ipc_msg_name(func)
in_args, out_args = func.get_args_categories()
ipc_in_args = [cpp_arg(webkit_ipc_types[a.type], a.name) for a in in_args]
ipc_out_args = []
if not func.return_type.is_void():
ipc_out_args += [cpp_arg(webkit_ipc_types[func.return_type], "returnValue")]
for a in out_args:
ipc_out_args += [cpp_arg(webkit_ipc_types[a.type], a.name)]
if a.type.is_dynamic_span():
ipc_in_args += [cpp_arg(get_cpp_type("uint64_t"), f"{a.name}Size")]
self.in_args = cpp_arg_list(ipc_in_args)
self.out_args = cpp_arg_list(ipc_out_args)
def __str__(self):
if len(self.out_args.args):
return f"\n void {self.name}({str(self.in_args)}) -> ({str(self.out_args)}) Synchronous"
return f"\n void {self.name}({str(self.in_args)})"
class webkit_ipc_cpp_proxy_impl(object):
name: str
msg_name: str
return_type: cpp_type
args: cpp_arg_list
pre_call_stmts: List[str]
call_stmts: List[str]
post_call_stmts: List[str]
return_stmts: List[str]
in_exprs: List[cpp_expr]
out_exprs: List[cpp_expr]
def __init__(self, cpp_func: cpp_func):
self.name = cpp_func.name
self.msg_name = webkit_ipc_msg_name(cpp_func)
self.return_type = cpp_func.return_type
self.args = cpp_func.args
self.pre_call_stmts = []
self.call_stmts = []
self.post_call_stmts = []
self.return_stmts = []
self.in_exprs = []
self.out_exprs = []
in_args, out_args = cpp_func.get_args_categories()
self.process_return_value(cpp_func.return_type)
self.process_in_args(in_args)
self.process_out_args(out_args)
self.process_call()
def process_call(self):
in_exprs = ", ".join([str(i) for i in self.in_exprs])
out_exprs = ", ".join(str(o) for o in self.out_exprs)
is_async = self.return_type.is_void() and len(out_exprs) == 0
self.call_stmts += [ "if (!isContextLost()) {" ]
if is_async:
self.call_stmts += [
f" auto sendResult = send(Messages::RemoteGraphicsContextGL::{self.msg_name}({in_exprs}));",
]
else:
self.call_stmts += [
f" auto sendResult = sendSync(Messages::RemoteGraphicsContextGL::{self.msg_name}({in_exprs}), Messages::RemoteGraphicsContextGL::{self.msg_name}::Reply({out_exprs}));"
]
self.call_stmts += [
" if (!sendResult)",
" markContextLost();",
]
if self.post_call_stmts:
self.call_stmts += [" else {"] if len(self.post_call_stmts) > 1 else [ " else"]
self.post_call_stmts = [ " " + l for l in self.post_call_stmts ]
self.post_call_stmts += [ " }" ] if len(self.post_call_stmts) > 1 else []
self.post_call_stmts += [ "}" ]
def process_in_args(self, in_args: List[cpp_arg]):
for i in in_args:
if i.type.is_const_pointer():
assert False
else:
self.in_exprs += [webkit_ipc_convert_expr(cpp_expr(i.type, i.name), webkit_ipc_types[i.type])]
def process_out_args(self, out_args: List[cpp_arg]):
for o in out_args:
if o.type.is_pointer():
value_type = o.type.get_value_type()
webkit_ipc_value_type = webkit_ipc_types[value_type]
v = cpp_arg(webkit_ipc_value_type, o.name + "Reply")
self.pre_call_stmts += [f"{str(v.type)} {str(v.name)} = {{ }};"]
e = cpp_expr(v.type, v.name)
self.out_exprs += [e]
self.post_call_stmts += [
#fmt: off
f" if ({o.name})",
f" *{o.name} = {webkit_ipc_convert_expr(e, value_type)};"
#fmt: on
]
elif o.type.is_span():
webkit_ipc_type = webkit_ipc_types[o.type]
assert(isinstance(webkit_ipc_type, cpp_type_container))
v = cpp_arg(webkit_ipc_type, o.name + "Reply")
self.pre_call_stmts += [f"{str(v.type)} {str(v.name)};"]
self.out_exprs += [cpp_expr(v.type, v.name)]
if o.type.is_dynamic_span():
self.in_exprs += [cpp_expr(get_cpp_type("size_t"), f"{o.name}.bufSize")]
self.post_call_stmts += [
f" memcpy({o.name}.data, {v.name}.data(), {o.name}.bufSize * sizeof({str(webkit_ipc_type.get_contained_type())}));"
]
else:
self.out_exprs += [cpp_expr(o.type, o.name)]
def process_return_value(self, return_type: cpp_type):
if return_type.is_void():
return
self.return_type = return_type
ipc_return_type = webkit_ipc_types[return_type]
return_value_expr = cpp_expr(ipc_return_type, "returnValue")
self.pre_call_stmts += [f"{return_value_expr.type} {return_value_expr.expr} = {{ }};"]
self.out_exprs = [return_value_expr] + self.out_exprs
self.return_stmts = [f"return {str(webkit_ipc_convert_expr(return_value_expr, return_type))};"]
def __str__(self):
nolint = " // NOLINT" if "_" in self.name else ""
body = "".join(f"\n {b}" for b in self.pre_call_stmts + self.call_stmts + self.post_call_stmts + self.return_stmts)
return f"""\n{str(self.return_type)} RemoteGraphicsContextGLProxy::{self.name}({str(self.args)}){nolint}
{{{body}
}}"""
class webkit_ipc_cpp_proxy_placeholder(object):
name: str
return_type: cpp_type
args: cpp_arg_list
body: List[str]
def __init__(self, cpp_func: cpp_func):
self.name = cpp_func.name
self.return_type = cpp_func.return_type
self.args = cpp_func.args
self.body = ["notImplemented();"]
if cpp_func.return_type.type_name != "void":
self.body += ["return { };"]
def __str__(self):
nolint = " // NOLINT" if "_" in self.name else ""
body = "\n{\n " + "\n ".join(self.body) + "\n}"
return f"\n{str(self.return_type)} RemoteGraphicsContextGLProxy::{self.name}({str(self.args)}){nolint}{body}"
class context_proxy_cpp_webkit_ipc_generator(object):
"RemoteGraphicsContextGLProxy C++ implementation generator."
impls: List[webkit_ipc_cpp_proxy_impl]
unimpls: List[webkit_ipc_cpp_proxy_placeholder]
def __init__(self, funcs: Iterable[cpp_func], unimplemented: Iterable[cpp_func]):
self.impls = [webkit_ipc_cpp_proxy_impl(f) for f in funcs]
self.unimpls = [webkit_ipc_cpp_proxy_placeholder(f) for f in unimplemented]
def get_functions(self):
return "\n".join(str(i) for i in self.impls + self.unimpls)
def generate(self):
write_file(
context_proxy_functions_fn,
context_proxy_functions_template.format(self.get_functions()),
)
class webkit_ipc_cpp_impl(object):
name: str
args: cpp_arg_list
is_extension: bool
pre_call_stmts: List[str]
call_stmts: List[str]
post_call_stmts: List[str]
in_exprs: List[cpp_expr]
out_exprs: List[cpp_expr]
return_value_expr: Optional[cpp_expr]
def __init__(self, cpp_func: cpp_func):
self.name = cpp_func.name
self.overload_suffix = cpp_func.overload_suffix
self.args = cpp_arg_list([])
self.is_extension = cpp_func.is_extension
self.pre_call_stmts = []
self.call_stmts = []
self.post_call_stmts = []
self.in_exprs = []
self.out_exprs = []
self.return_value_expr = None
self.process_return_value(cpp_func.return_type)
in_args, out_args = cpp_func.get_args_categories()
self.process_args(cpp_func.args.args, set(in_args), set(out_args))
self.process_call()
def process_call(self):
self.call_stmts += [ "assertIsCurrent(m_streamThread);" ]
ext = "getExtensions()." if self.is_extension else ""
in_exprs = ", ".join(str(e) for e in self.in_exprs)
is_async = len(self.out_exprs) == 0
call_expr = f"m_context->{ext}{self.name}({in_exprs})"
if not self.return_value_expr:
self.call_stmts += [f"{call_expr};"]
else:
self.call_stmts += [f"{str(self.return_value_expr)} = {call_expr};"]
if not is_async:
out_exprs = ", ".join(str(e) for e in self.out_exprs)
self.post_call_stmts += [f"completionHandler({out_exprs});"]
def process_args(self, args: List[cpp_arg], in_args: Set[cpp_arg], out_args: Set[cpp_arg]):
for a in args:
if a.type.is_const_pointer():
assert False
if a in in_args:
self.process_in_arg(a)
else:
self.process_out_arg(a)
if self.out_exprs:
out_arg_decls = ", ".join(f"{str(e.type)}" for e in self.out_exprs)
self.args.args += [cpp_arg(get_cpp_type(f"CompletionHandler<void({out_arg_decls})>&&"), "completionHandler")]
def process_in_arg(self, a: cpp_arg):
self.args.args += [cpp_arg(webkit_ipc_get_message_forwarder_type(webkit_ipc_types[a.type]), a.name)]
self.in_exprs += [webkit_ipc_convert_expr(cpp_expr(webkit_ipc_types[a.type], a.name), a.type)]
def process_out_arg(self, a: cpp_arg):
if a.type.is_pointer():
value_arg = cpp_arg(a.type.get_value_type(), a.name)
self.pre_call_stmts += [f"{str(value_arg.type)} {str(value_arg.name)} = {{ }};"]
self.in_exprs += [cpp_expr(a.type, f"&{value_arg.name}")]
e = cpp_expr(value_arg.type, value_arg.name)
webkit_ipc_value_type = webkit_ipc_types[e.type]
self.out_exprs += [webkit_ipc_convert_expr(e, webkit_ipc_value_type)]
elif a.type.is_dynamic_span():
assert(isinstance(a.type, cpp_type_container))
size_arg = cpp_arg(get_cpp_type("uint64_t"), f"{a.name}Size")
self.args.args += [size_arg]
store_arg = cpp_arg(webkit_ipc_get_span_store_type(a.type), a.name)
self.pre_call_stmts += [f"{str(store_arg.type)} {str(store_arg.name)}(static_cast<size_t>({a.name}Size), 0);"]
self.in_exprs += [cpp_expr(store_arg.type, f"{store_arg.name}")]
webkit_ipc_type = webkit_ipc_types[a.type]
self.out_exprs += [webkit_ipc_convert_expr(cpp_expr(store_arg.type, store_arg.name), webkit_ipc_type)]
elif a.type.is_span():
assert(isinstance(a.type, cpp_type_container))
store_arg = cpp_arg(webkit_ipc_get_span_store_type(a.type), a.name)
self.pre_call_stmts += [f"{str(store_arg.type)} {str(store_arg.name)} {{ }};"]
self.in_exprs += [cpp_expr(store_arg.type, store_arg.name)]
webkit_ipc_type = webkit_ipc_types[a.type]
self.out_exprs += [webkit_ipc_convert_expr(cpp_expr(store_arg.type, store_arg.name), webkit_ipc_type)]
else:
assert a.type.is_reference()
webkit_ipc_type = webkit_ipc_types[a.type]
value_arg = cpp_arg(a.type.get_value_type(), a.name)
self.pre_call_stmts += [f"{self.ns(value_arg.type)} {str(value_arg.name)} {{ }};"]
e = cpp_expr(value_arg.type, value_arg.name)
self.in_exprs += [e]
self.out_exprs += [webkit_ipc_move_expr(webkit_ipc_convert_expr(e, webkit_ipc_type))]
def process_return_value(self, return_type: cpp_type):
if return_type.is_void():
return
ipc_return_type = webkit_ipc_types[return_type]
return_value_expr = cpp_expr(return_type, "returnValue")
self.pre_call_stmts += [f"{return_value_expr.type} {return_value_expr.expr} = {{ }};"]
self.out_exprs += [webkit_ipc_move_expr(webkit_ipc_convert_expr(return_value_expr, ipc_return_type))]
self.return_value_expr = return_value_expr
def __str__(self):
body = "".join(f"\n {b}" for b in self.pre_call_stmts + self.call_stmts + self.post_call_stmts)
return f""" void {self.name}{self.overload_suffix}({str(self.args)})
{{{body}
}}"""
def ns(self, type: cpp_type):
# TODO: namespace info is not stored in names.
if type.type_name == "ActiveInfo":
return "WebCore::GraphicsContextGL::" + str(type)
return str(type)
def webkit_ipc_resolve_overload_suffix(funcs : Iterable[cpp_func]):
counts = collections.Counter(f.name for f in funcs)
suffixes : Counter[str] = collections.Counter()
for f in funcs:
if counts[f.name] > 1:
f.overload_suffix = str(suffixes[f.name])
suffixes.update([f.name])
class context_cpp_webkit_ipc_generator(object):
"RemoteGraphicsContextGL C++ implementation generator."
impls: List[webkit_ipc_cpp_impl]
def __init__(self, funcs: Iterable[cpp_func]):
self.impls = [webkit_ipc_cpp_impl(f) for f in funcs]
def get_functions(self):
return "\n".join(str(i) for i in self.impls)
def generate(self):
write_file(
context_functions_fn,
context_functions_template.format(self.get_functions()),
)
class context_msg_webkit_ipc_generator(object):
"RemoteGraphicsContextGL WebKit IPC message definition generator."
msgs: List[webkit_ipc_msg]
def __init__(self, funcs: Iterable[cpp_func]):
self.msgs = [webkit_ipc_msg(f) for f in funcs]
def get_messages(self):
return "".join(str(m) for m in self.msgs)
def generate(self):
write_file(context_messages_fn, context_messages_template.format(self.get_messages()))
def create_cpp_arg(arg_spec: str, arg_index: int):
r = arg_spec.rsplit(" ", 1)
name = ""
type = None
if len(r) > 1 and r[0] != "const":
name = r[1].strip()
type = get_cpp_type(r[0])
else:
name = "arg{}".format(arg_index)
type = get_cpp_type(arg_spec)
return cpp_arg(type, name)
def create_cpp_arg_list(args_specs: Iterable[str]):
return cpp_arg_list([create_cpp_arg(arg_spec=e, arg_index=i) for i, e in enumerate(args_specs)])
def create_cpp_func(name: str, return_spec: str, args_specs: List[str], is_extension: bool):
return cpp_func(name=name, return_type=get_cpp_type(return_spec), args=create_cpp_arg_list(args_specs), is_extension=is_extension)
def read_lines_until(lines : Iterable[str], match : str) -> Generator[str, None, None]:
for line in lines:
if re.match(match, line.strip()):
break
yield line
def main():
parser = argparse.ArgumentParser("usage: %prog [options]")
parser.add_argument("--verbose", action="store_true", help="Print some debug info from the generator script")
options = parser.parse_args()
# Construct the list of functions to remote from web process to GPU process.
# FIXME: Until extensions header is removed, we need to complicate the implementation
# with the distinction of whether a function is or is not an extension function.
extension_signatures : Set[str] = set()
with open(extensions_input_fn) as f:
for line in f.readlines():
m = re.match(r".*virtual (\S+)\s+(\w+)\((.*)\) = 0;", line)
if m:
func = create_cpp_func(
name=m[2],
return_spec=m[1],
args_specs=cpp_split_args_specs(m[3]),
is_extension=True,
)
extension_signatures.update([str(func)])
funcs = []
unimplemented = []
for fn in functions_input_fns:
with open(fn) as f:
lines = iter(f.readlines())
for _ in read_lines_until(lines, "// Functions with a generated implementation. This list is used by generate-gpup-webgl script."):
pass
for line in read_lines_until(lines, "// End of list used by generate-gpup-webgl script."):
m = re.match(r"\s*(\S+)\s+(\w+)\((.*)\) final;", line)
if m:
func_name = m[2]
func = create_cpp_func(
name=func_name,
return_spec=m[1],
args_specs=cpp_split_args_specs(m[3]),
is_extension=False,
)
func.is_extension = str(func) in extension_signatures
if func.is_implemented():
funcs.append(func)
else:
unimplemented.append(func)
webkit_ipc_resolve_overload_suffix(funcs)
# Initialize WebKit IPC generation. Construct type mappings for the used C++ types -> IPC types.
# TODO: namespacing issues.
ai_type = get_cpp_type("ActiveInfo&")
ai_transfer_type = get_cpp_type("WebCore::GraphicsContextGL::ActiveInfo")
webkit_ipc_types[ai_type] = ai_transfer_type
with open(types_input_fn) as f:
lines = iter(f.readlines())
for _ in read_lines_until(lines, "// The GCGL types map to following WebKit IPC types. The list is used by generate-gpup-webgl script."):
pass
for line in read_lines_until(lines, "// End of list used by generate-gpup-webgl script."):
m = re.match(r".*static_assert\(std::is_same_v<(\w+), (\w+)>\);", line)
if m:
webkit_ipc_types[get_cpp_type(m[1])] = get_cpp_type(m[2])
continue
m = re.match(r".*static_assert\(sizeof\((\w+)\) == sizeof\((\w+)\)\);", line)
if m:
cpp_type = get_cpp_type(m[1])
ipc_type = get_cpp_type(m[2])
webkit_ipc_types[cpp_type] = ipc_type
webkit_ipc_types_converters[(cpp_type, ipc_type)] = cpp_static_cast
webkit_ipc_types_converters[(ipc_type, cpp_type)] = cpp_static_cast
continue
# FIXME: Here we use <= instead of == due to pointers, such as GCGLsync, GCGLintptr, ....
# This ends up failing if there ever is arrays of these types, since arrays are currently
# reinterpret-casted.
# WebKit IPC requires one sized type, but the size of these pointer-based values will change
# based on the platform.
m = re.match(r".*static_assert\(sizeof\((\w+)\) <= sizeof\((\w+)\)\);", line)
if m:
cpp_type = get_cpp_type(m[1])
ipc_type = get_cpp_type(m[2])
webkit_ipc_types[cpp_type] = ipc_type
webkit_ipc_types_converters[(cpp_type, ipc_type)] = cpp_static_cast
webkit_ipc_types_converters[(ipc_type, cpp_type)] = cpp_static_cast
continue
m = re.match(r".*static_assert\(sizeof\((\w+)\) <= sizeof\((\w+)\) && sizeof\((\w+)\) == sizeof\((\w+)\)\);", line)
if m:
cpp_type = get_cpp_type(m[1])
ipc_type = get_cpp_type(m[2])
cpp_cast_through_type = get_cpp_type(m[4])
webkit_ipc_types[cpp_type] = ipc_type
webkit_ipc_types_converters[(cpp_type, ipc_type)] = cpp_reinterpret_cast_from_pointer_through(cpp_cast_through_type)
webkit_ipc_types_converters[(ipc_type, cpp_type)] = cpp_reinterpret_cast_to_pointer_through(cpp_cast_through_type)
continue
# For all types in the input API, form corresponding IPC types and conversions.
# TODO: this is not driven by the typing.
all_types = set()
for func in funcs:
all_types.update(func.args.types() + [func.return_type])
# Sort by len so that template args come before templates.
for cpp_type in sorted(all_types, key=lambda x: len(x.type_name)):
if cpp_type in webkit_ipc_types:
continue
if cpp_type.is_dynamic_span() or cpp_type.is_span():
transfer_type = webkit_ipc_get_span_transfer_type(cpp_type)
webkit_ipc_types[cpp_type] = transfer_type.get_value_type()
if cpp_type.get_contained_type().is_const():
webkit_ipc_types_converters[(cpp_type, transfer_type)] = webkit_ipc_span_transfer_type_reinterpret_cast_conversion
webkit_ipc_types_converters[(transfer_type, cpp_type)] = webkit_ipc_make_gcglspan_conversion
else:
store_type = webkit_ipc_get_span_store_type(cpp_type)
webkit_ipc_types_converters[(store_type, transfer_type)] = cpp_array_reinterpret_cast_conversion
elif cpp_type.is_reference() or cpp_type.is_const_reference() or cpp_type.is_container():
value_type = cpp_type.get_value_type() if not cpp_type.is_container() else cpp_type
if value_type.is_container():
contained_type = value_type.get_contained_type()
webkit_ipc_contained_type = webkit_ipc_types[contained_type]
if webkit_ipc_contained_type == contained_type:
webkit_ipc_types[cpp_type] = value_type
else:
transfer_type = get_cpp_type(f"{value_type.get_container_name()}<{str(webkit_ipc_contained_type)}>")
webkit_ipc_types[cpp_type] = transfer_type
# TODO: Here might be case where Vector<GCGLint> was used in the interface and Vector<int32_t> is used
# in the IPC definition. There is no sensible way to do this at the moment. We do not convert, rather
# expect that all the invocations are taken care of the fact that GCGLint is the same type as int32_t.
# This will not hold for all types such as GCGLboolean.
#webkit_ipc_types_converters[(cpp_type, transfer_type)] = cpp_array_reinterpret_cast_conversion
else:
webkit_ipc_types[cpp_type] = value_type
else:
webkit_ipc_types[cpp_type] = cpp_type.get_value_type()
# The input to the generators is list of functions to remote.
# Run the various generators.
generators = [
context_proxy_cpp_webkit_ipc_generator(funcs, unimplemented),
context_msg_webkit_ipc_generator(funcs),
context_cpp_webkit_ipc_generator(funcs),
]
for g in generators:
g.generate()
if options.verbose:
print("Implemented:")
print("".join(f" {str(i)} final\n" for i in funcs))
print("Unimplemented:")
print("".join(f" {str(u)} final\n" for u in unimplemented))
print(f"Implemented: {len(funcs)}, unimplemented: {len(unimplemented)}, total: {len(funcs)+len(unimplemented)}")
print("C++ type map to WebKit IPC type map:")
for _, t in cpp_types.items():
if t in webkit_ipc_types:
print(f" {str(t)} -> {str(webkit_ipc_types[t])}")
print("C++ types without WebKit IPC type:")
for _, t in cpp_types.items():
if t not in webkit_ipc_types:
print(f" {str(t)}")
print("WebKit IPC type conversions")
for (a, b), converter in webkit_ipc_types_converters.items():
print(f" {str(a)} -> {str(b)} : {converter.__name__}")
print("Done.")
return 0
if __name__ == "__main__":
sys.exit(main())