| #!/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()) |