#!/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() 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 USE(GRAPHICS_LAYER_WC) void PrepareForDisplay() -> (std::optional contentBuffer) Synchronous #endif #if USE(LIBGBM) void PrepareForDisplay() -> (WebCore::DMABufObject dmabufObject) Synchronous NotStreamEncodableReply #endif #if !PLATFORM(COCOA) && !USE(GRAPHICS_LAYER_WC) && !USE(LIBGBM) void PrepareForDisplay() -> () Synchronous #endif void EnsureExtensionEnabled(String extension) void MarkContextChanged() void SynthesizeGLError(uint32_t error) void GetError() -> (uint32_t returnValue) Synchronous void PaintRenderingResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer) -> () Synchronous void PaintCompositedResultsToCanvas(WebCore::RenderingResourceIdentifier imageBuffer) -> () Synchronous #if ENABLE(VIDEO) void CopyTextureFromVideoFrame(WebKit::RemoteVideoFrameReadReference videoFrame, 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 #endif #if ENABLE(MEDIA_STREAM) void PaintCompositedResultsToVideoFrame() -> (std::optional properties) Synchronous #endif void SimulateEventForTesting(WebCore::GraphicsContextGL::SimulatedEventForTesting event) void ReadnPixels0(int32_t x, int32_t y, int32_t width, int32_t height, uint32_t format, uint32_t type, IPC::ArrayReference data) -> (IPC::ArrayReference data) Synchronous void ReadnPixels1(int32_t x, int32_t y, int32_t width, int32_t height, uint32_t format, uint32_t type, uint64_t offset) void MultiDrawArraysANGLE(uint32_t mode, IPC::ArrayReferenceTuple firstsAndCounts) void MultiDrawArraysInstancedANGLE(uint32_t mode, IPC::ArrayReferenceTuple firstsCountsAandInstanceCounts) void MultiDrawElementsANGLE(uint32_t mode, IPC::ArrayReferenceTuple countsAndOffsets, uint32_t type) void MultiDrawElementsInstancedANGLE(uint32_t mode, IPC::ArrayReferenceTuple countsOffsetsAndInstanceCounts, uint32_t type) void PaintRenderingResultsToPixelBuffer() -> (std::optional returnValue) Synchronous {} }} #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) or self.container_name == "GCGLSpanTuple" 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', 'RetainPtr', 'std::optional']: 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({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 overload_suffix: str def __init__(self, name: str, return_type: cpp_type, args: cpp_arg_list): self.name = name self.return_type = return_type self.args = args 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") if o.type.is_dynamic_span(): self.pre_call_stmts += [f"{str(v.type)} {str(v.name)};"] else: self.pre_call_stmts += [ f"constexpr std::array<{str(v.type.get_contained_type())}, {str(v.type.get_arity())}> {str(v.name)}Empty {{ }};", f"{str(v.type)} {str(v.name)} {{ {str(v.name)}Empty }};" ] 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 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.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(workQueue());" ] in_exprs = ", ".join(str(e) for e in self.in_exprs) is_async = len(self.out_exprs) == 0 call_expr = f"m_context->{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&&"), "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({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): 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) return f""" void {self.name}{self.overload_suffix}({str(self.args)}){nolint} {{{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]): return cpp_func(name=name, return_type=get_cpp_type(return_spec), args=create_cpp_arg_list(args_specs)) 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. 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]), ) 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 was used in the interface and Vector 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())