Introduce new logging API, fixes #858

PiperOrigin-RevId: 930744288
Change-Id: I6ec1203b55c031390f3eef23192e2337508ce886
This commit is contained in:
Yuval Tassa
2026-06-11 14:36:17 -07:00
committed by Copybara-Service
parent a2abaf7aef
commit 58f6d52491
45 changed files with 2586 additions and 422 deletions
+18 -1
View File
@@ -15,7 +15,9 @@
"""Utility functions for code generation."""
import os
from typing import Union
from introspect import ast_nodes
from wasm.codegen.generators import constants
def write_to_file(filepath: str, content: str) -> None:
@@ -87,12 +89,27 @@ def get_inner_value_type(
return param.type.inner_type
def is_struct_value_type(
t: Union[ast_nodes.ValueType, ast_nodes.ArrayType, ast_nodes.PointerType],
) -> bool:
"""Checks if a type is a struct passed by value."""
if isinstance(t, ast_nodes.ValueType):
return (
t.name not in constants.PRIMITIVE_TYPES
and t.name != "void"
and not t.name.startswith("mjf")
)
return False
def should_be_wrapped(func: ast_nodes.FunctionDecl) -> bool:
"""Checks if a MuJoCo function needs a wrapper function."""
if get_pointer_return_inner_value_type(func):
return True
if is_struct_value_type(func.return_type):
return True
for param in func.parameters:
if get_inner_value_type(param):
if get_inner_value_type(param) or is_struct_value_type(param.type):
return True
return False
+4
View File
@@ -149,6 +149,7 @@ _SKIPPED_MEMORY_FUNCTIONS: tuple[str, ...] = (
"mju_error",
"mju_free",
"mju_malloc",
"mju_setLogHandler",
"mju_strncpy",
"mju_warning",
# go/keep-sorted end
@@ -230,6 +231,7 @@ MANUAL_WRAPPER_FUNCTIONS: tuple[str, ...] = (
"mj_saveModel",
"mj_setLengthRange",
"mju_error",
"mju_info",
# go/keep-sorted end
)
@@ -276,6 +278,8 @@ STRUCTS_TO_BIND: list[str] = list(
NO_DEFAULT_CONSTRUCTORS: tuple[str, ...] = (
# go/keep-sorted start
"mjContact",
"mjLogConfig",
"mjLogMessage",
"mjPreContact",
"mjSolverStat",
"mjStatistic",
+21
View File
@@ -154,6 +154,14 @@ def get_param_string(p: ast_nodes.FunctionParameterDecl) -> str:
return f"const NumberArray& {p.name}"
else:
return f"const val& {p.name}"
elif common.is_struct_value_type(p.type):
# Struct by value parameters
value_type = cast(ast_nodes.ValueType, p.type)
const_qualifier = "const " if value_type.is_const else "const "
return (
f"{const_qualifier}{common.capitalize(value_type.name)}&"
f" {p.name}"
)
else:
# This case should ideally not be reached if AST is well-formed
# and types are categorized by the helper booleans correctly.
@@ -183,6 +191,8 @@ def get_params_string_maybe_with_conversion(
native_params.append(f"{p.name}.get()")
elif param_is_primitive_value(p):
native_params.append(p.name)
elif common.is_struct_value_type(p.type):
native_params.append(f"*{p.name}.get()")
else:
raise TypeError(
f"Unhandled parameter type for conversion: {p.type} for param"
@@ -201,6 +211,14 @@ def get_compatible_return_code(func: ast_nodes.FunctionDecl) -> str:
return f"{c_call};"
if func.return_type.name in constants.PRIMITIVE_TYPES:
return f"return {c_call};"
if common.is_struct_value_type(func.return_type):
struct_name = func.return_type.name
w = common.wrapped_struct_name(struct_name)
builder = code_builder.CodeBuilder()
builder.line(f"{w} result;")
builder.line(f"*result.get() = {c_call};")
builder.line("return result;")
return builder.to_string()
if inner_type := common.get_pointer_return_inner_value_type(func):
if inner_type.name == "char":
@@ -230,6 +248,9 @@ def get_compatible_return_type(func: ast_nodes.FunctionDecl) -> str:
and func.return_type.name in constants.PRIMITIVE_TYPES
):
return f"{func.return_type.name}"
if common.is_struct_value_type(func.return_type):
return_type = cast(ast_nodes.ValueType, func.return_type)
return common.wrapped_struct_name(return_type.name)
return "val"
+30
View File
@@ -229,6 +229,36 @@ def _generate_field_data(
binding=_get_property_binding(f, w, setter=False, reference=True),
)
# Case 2.5: const char* without array_extent (C strings like body, func).
elif (
inner_type_name == "char"
and not is_dynamically_sized
):
builder = code_builder.CodeBuilder()
if f.type.inner_type.is_const:
with builder.function(f"std::string {f.name}() const"):
builder.line(
f'return ptr_->{f.name} ? std::string(ptr_->{f.name}) : "";'
)
else:
with builder.function(f"std::string {f.name}() const"):
builder.line(
f'return ptr_->{f.name} ? std::string(ptr_->{f.name}) : "";'
)
with builder.function(f"void set_{f.name}(const std::string& value)"):
with builder.block(f"if (ptr_->{f.name})"):
builder.line(
f"std::strncpy(ptr_->{f.name}, value.c_str(),"
f" sizeof(ptr_->{f.name}));"
)
return WrappedFieldData(
declaration=builder.to_string(),
typename=_get_field_struct_type(f, s),
binding=_get_property_binding(
f, w, setter=not f.type.inner_type.is_const, reference=True
),
)
# Case 3: Non-dynamically sized pointer fields to other structs.
elif (
not is_dynamically_sized