Simplified struct field handling in the WASM bindings
PiperOrigin-RevId: 829003969 Change-Id: I139f9c0dad96e1664b1bd054f09fcdf58aff260e
This commit is contained in:
committed by
Copybara-Service
parent
06ecdd6ba1
commit
7dfd92098f
@@ -4349,6 +4349,9 @@ struct MjModel {
|
|||||||
void set_nbuffer(mjtSize value) {
|
void set_nbuffer(mjtSize value) {
|
||||||
ptr_->nbuffer = value;
|
ptr_->nbuffer = value;
|
||||||
}
|
}
|
||||||
|
// opt field is handled manually in template file struct declaration
|
||||||
|
// vis field is handled manually in template file struct declaration
|
||||||
|
// stat field is handled manually in template file struct declaration
|
||||||
emscripten::val buffer() const {
|
emscripten::val buffer() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nbuffer, static_cast<uint8_t*>(ptr_->buffer)));
|
return emscripten::val(emscripten::typed_memory_view(ptr_->nbuffer, static_cast<uint8_t*>(ptr_->buffer)));
|
||||||
}
|
}
|
||||||
@@ -5729,7 +5732,7 @@ struct MjData {
|
|||||||
void set_maxuse_efc(int value) {
|
void set_maxuse_efc(int value) {
|
||||||
ptr_->maxuse_efc = value;
|
ptr_->maxuse_efc = value;
|
||||||
}
|
}
|
||||||
// array field is defined manually. solver
|
// solver field is handled manually in template file struct declaration
|
||||||
emscripten::val solver_niter() const {
|
emscripten::val solver_niter() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(20, ptr_->solver_niter));
|
return emscripten::val(emscripten::typed_memory_view(20, ptr_->solver_niter));
|
||||||
}
|
}
|
||||||
@@ -5739,8 +5742,8 @@ struct MjData {
|
|||||||
emscripten::val solver_fwdinv() const {
|
emscripten::val solver_fwdinv() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(2, ptr_->solver_fwdinv));
|
return emscripten::val(emscripten::typed_memory_view(2, ptr_->solver_fwdinv));
|
||||||
}
|
}
|
||||||
// array field is defined manually. warning
|
// warning field is handled manually in template file struct declaration
|
||||||
// array field is defined manually. timer
|
// timer field is handled manually in template file struct declaration
|
||||||
int ncon() const {
|
int ncon() const {
|
||||||
return ptr_->ncon;
|
return ptr_->ncon;
|
||||||
}
|
}
|
||||||
@@ -6077,7 +6080,7 @@ struct MjData {
|
|||||||
emscripten::val cfrc_ext() const {
|
emscripten::val cfrc_ext() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cfrc_ext));
|
return emscripten::val(emscripten::typed_memory_view(model->nbody * 6, ptr_->cfrc_ext));
|
||||||
}
|
}
|
||||||
// complex pointer field is defined manually. contact
|
// contact field is handled manually in template file struct declaration
|
||||||
emscripten::val efc_type() const {
|
emscripten::val efc_type() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_type));
|
return emscripten::val(emscripten::typed_memory_view(ptr_->nefc, ptr_->efc_type));
|
||||||
}
|
}
|
||||||
@@ -6380,26 +6383,26 @@ struct MjvScene {
|
|||||||
void set_ngeom(int value) {
|
void set_ngeom(int value) {
|
||||||
ptr_->ngeom = value;
|
ptr_->ngeom = value;
|
||||||
}
|
}
|
||||||
// complex pointer field is defined manually. geoms
|
// geoms field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. geomorder
|
// geomorder field is handled manually in template file struct declaration
|
||||||
int nflex() const {
|
int nflex() const {
|
||||||
return ptr_->nflex;
|
return ptr_->nflex;
|
||||||
}
|
}
|
||||||
void set_nflex(int value) {
|
void set_nflex(int value) {
|
||||||
ptr_->nflex = value;
|
ptr_->nflex = value;
|
||||||
}
|
}
|
||||||
// primitive pointer field with complex extents is defined manually. flexedgeadr
|
// flexedgeadr field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexedgenum
|
// flexedgenum field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexvertadr
|
// flexvertadr field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexvertnum
|
// flexvertnum field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexfaceadr
|
// flexfaceadr field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexfacenum
|
// flexfacenum field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexfaceused
|
// flexfaceused field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexedge
|
// flexedge field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexvert
|
// flexvert field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexface
|
// flexface field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flexnormal
|
// flexnormal field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. flextexcoord
|
// flextexcoord field is handled manually in template file struct declaration
|
||||||
mjtByte flexvertopt() const {
|
mjtByte flexvertopt() const {
|
||||||
return ptr_->flexvertopt;
|
return ptr_->flexvertopt;
|
||||||
}
|
}
|
||||||
@@ -6430,19 +6433,19 @@ struct MjvScene {
|
|||||||
void set_nskin(int value) {
|
void set_nskin(int value) {
|
||||||
ptr_->nskin = value;
|
ptr_->nskin = value;
|
||||||
}
|
}
|
||||||
// primitive pointer field with complex extents is defined manually. skinfacenum
|
// skinfacenum field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. skinvertadr
|
// skinvertadr field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. skinvertnum
|
// skinvertnum field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. skinvert
|
// skinvert field is handled manually in template file struct declaration
|
||||||
// primitive pointer field with complex extents is defined manually. skinnormal
|
// skinnormal field is handled manually in template file struct declaration
|
||||||
int nlight() const {
|
int nlight() const {
|
||||||
return ptr_->nlight;
|
return ptr_->nlight;
|
||||||
}
|
}
|
||||||
void set_nlight(int value) {
|
void set_nlight(int value) {
|
||||||
ptr_->nlight = value;
|
ptr_->nlight = value;
|
||||||
}
|
}
|
||||||
// array field is defined manually. lights
|
// lights field is handled manually in template file struct declaration
|
||||||
// array field is defined manually. camera
|
// camera field is handled manually in template file struct declaration
|
||||||
mjtByte enabletransform() const {
|
mjtByte enabletransform() const {
|
||||||
return ptr_->enabletransform;
|
return ptr_->enabletransform;
|
||||||
}
|
}
|
||||||
@@ -6505,7 +6508,7 @@ struct MjSpec {
|
|||||||
std::unique_ptr<MjSpec> copy();
|
std::unique_ptr<MjSpec> copy();
|
||||||
mjSpec* get() const;
|
mjSpec* get() const;
|
||||||
void set(mjSpec* ptr);
|
void set(mjSpec* ptr);
|
||||||
// complex pointer field is defined manually. element
|
// element field is handled manually in template file struct declaration
|
||||||
mjString modelname() const {
|
mjString modelname() const {
|
||||||
return (ptr_ && ptr_->modelname) ? *(ptr_->modelname) : "";
|
return (ptr_ && ptr_->modelname) ? *(ptr_->modelname) : "";
|
||||||
}
|
}
|
||||||
@@ -6514,16 +6517,16 @@ struct MjSpec {
|
|||||||
*(ptr_->modelname) = value;
|
*(ptr_->modelname) = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// struct field is defined manually. compiler
|
// compiler field is handled manually in template file struct declaration
|
||||||
mjtByte strippath() const {
|
mjtByte strippath() const {
|
||||||
return ptr_->strippath;
|
return ptr_->strippath;
|
||||||
}
|
}
|
||||||
void set_strippath(mjtByte value) {
|
void set_strippath(mjtByte value) {
|
||||||
ptr_->strippath = value;
|
ptr_->strippath = value;
|
||||||
}
|
}
|
||||||
// struct field is defined manually. option
|
// option field is handled manually in template file struct declaration
|
||||||
// struct field is defined manually. visual
|
// visual field is handled manually in template file struct declaration
|
||||||
// struct field is defined manually. stat
|
// stat field is handled manually in template file struct declaration
|
||||||
mjtSize memory() const {
|
mjtSize memory() const {
|
||||||
return ptr_->memory;
|
return ptr_->memory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,11 +61,14 @@ def write_to_file(filepath: str, content: str) -> None:
|
|||||||
print(f"Error writing to output file: {filepath} - {e}")
|
print(f"Error writing to output file: {filepath} - {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def lowercase_first_letter(input_string: str) -> str:
|
||||||
|
"""Lowercases the first letter of a string."""
|
||||||
|
return input_string[:1].lower() + input_string[1:]
|
||||||
|
|
||||||
|
|
||||||
def uppercase_first_letter(input_string: str) -> str:
|
def uppercase_first_letter(input_string: str) -> str:
|
||||||
"""Uppercases the first letter of a string."""
|
"""Uppercases the first letter of a string."""
|
||||||
if input_string:
|
return input_string[:1].upper() + input_string[1:]
|
||||||
return input_string[0].upper() + input_string[1:]
|
|
||||||
return input_string
|
|
||||||
|
|
||||||
|
|
||||||
def try_cast_to_scalar_type(value: str) -> int | float | str:
|
def try_cast_to_scalar_type(value: str) -> int | float | str:
|
||||||
|
|||||||
@@ -307,6 +307,17 @@ SKIPPED_STRUCTS: List[str] = [
|
|||||||
# go/keep-sorted end
|
# go/keep-sorted end
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# These structs require specific function calls for creation and/or deletion,
|
||||||
|
# or some of their fields need to be handled manually for now;
|
||||||
|
# making their wrapper constructors/destructors non-trivial.
|
||||||
|
MANUAL_STRUCTS: List[str] = [
|
||||||
|
"MjData",
|
||||||
|
"MjModel",
|
||||||
|
"MjvScene",
|
||||||
|
"MjSpec",
|
||||||
|
"MjVisual",
|
||||||
|
]
|
||||||
|
|
||||||
# Dictionary that maps anonymous structs to their parent struct and field name.
|
# Dictionary that maps anonymous structs to their parent struct and field name.
|
||||||
# Anonymous structs are not defined as independent structs in the MuJoCo
|
# Anonymous structs are not defined as independent structs in the MuJoCo
|
||||||
# codebase, but they are part of other structs. This dictionary is used to
|
# codebase, but they are part of other structs. This dictionary is used to
|
||||||
@@ -325,9 +336,9 @@ ANONYMOUS_STRUCTS: Dict[str, Dict[str, str]] = {
|
|||||||
# This list is created by subtracting the skipped structs from the list of all
|
# This list is created by subtracting the skipped structs from the list of all
|
||||||
# structs and adding the anonymous structs.
|
# structs and adding the anonymous structs.
|
||||||
STRUCTS_TO_BIND: List[str] = list(
|
STRUCTS_TO_BIND: List[str] = list(
|
||||||
(set(introspect_structs.STRUCTS.keys()) - set(SKIPPED_STRUCTS)).union(
|
set(introspect_structs.STRUCTS.keys())
|
||||||
ANONYMOUS_STRUCTS.keys()
|
.union(ANONYMOUS_STRUCTS.keys())
|
||||||
)
|
.difference(set(SKIPPED_STRUCTS))
|
||||||
)
|
)
|
||||||
|
|
||||||
# List of structs that do not have a default constructor.
|
# List of structs that do not have a default constructor.
|
||||||
@@ -421,37 +432,42 @@ MJDATA_SIZES: List[str] = [
|
|||||||
# go/keep-sorted end
|
# go/keep-sorted end
|
||||||
]
|
]
|
||||||
|
|
||||||
# Dictionary where keys are the struct names and the values are lists of the
|
# Fields that should be entirely omitted from the bindings.
|
||||||
# fields that are manually specified in the bindings.cc template file.
|
SKIPPED_FIELDS: Dict[str, List[str]] = {}
|
||||||
MANUALLY_ADDED_FIELDS_FROM_TEMPLATE: Dict[str, List[str]] = {
|
|
||||||
|
# Fields handled manually in template file struct declaration.
|
||||||
|
MANUAL_FIELDS: Dict[str, List[str]] = {
|
||||||
# go/keep-sorted start
|
# go/keep-sorted start
|
||||||
"MjData": ["solver", "timer", "warning", "contact"],
|
"MjData": ["solver", "timer", "warning", "contact"],
|
||||||
|
"MjModel": ["opt", "vis", "stat"],
|
||||||
"MjSpec": ["option", "visual", "stat", "element", "compiler"],
|
"MjSpec": ["option", "visual", "stat", "element", "compiler"],
|
||||||
"MjvScene": [
|
"MjvScene": [
|
||||||
"model",
|
# go/keep-sorted start
|
||||||
"lights",
|
|
||||||
"camera",
|
"camera",
|
||||||
"geoms",
|
"flexedge",
|
||||||
"geomorder",
|
|
||||||
"flexedgeadr",
|
"flexedgeadr",
|
||||||
"flexedgenum",
|
"flexedgenum",
|
||||||
"flexvertadr",
|
"flexface",
|
||||||
"flexvertnum",
|
|
||||||
"flexfaceadr",
|
"flexfaceadr",
|
||||||
"flexfacenum",
|
"flexfacenum",
|
||||||
"flexfaceused",
|
"flexfaceused",
|
||||||
"flexedge",
|
|
||||||
"flexvert",
|
|
||||||
"skinfacenum",
|
|
||||||
"skinvertadr",
|
|
||||||
"skinvertnum",
|
|
||||||
"skinvert",
|
|
||||||
"skinnormal",
|
|
||||||
"flexface",
|
|
||||||
"flexnormal",
|
"flexnormal",
|
||||||
"flextexcoord",
|
"flextexcoord",
|
||||||
|
"flexvert",
|
||||||
|
"flexvertadr",
|
||||||
|
"flexvertnum",
|
||||||
|
"geomorder",
|
||||||
|
"geoms",
|
||||||
|
"lights",
|
||||||
|
"model",
|
||||||
|
"skinfacenum",
|
||||||
|
"skinnormal",
|
||||||
|
"skinvert",
|
||||||
|
"skinvertadr",
|
||||||
|
"skinvertnum",
|
||||||
# go/keep-sorted end
|
# go/keep-sorted end
|
||||||
],
|
],
|
||||||
|
# go/keep-sorted end
|
||||||
}
|
}
|
||||||
|
|
||||||
# Dictionary that maps byte array fields to their corresponding size members.
|
# Dictionary that maps byte array fields to their corresponding size members.
|
||||||
@@ -462,14 +478,3 @@ BYTE_FIELDS: Dict[str, Dict[str, str]] = {
|
|||||||
"buffer": {"size": "nbuffer"},
|
"buffer": {"size": "nbuffer"},
|
||||||
"arena": {"size": "narena"},
|
"arena": {"size": "narena"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# These structs require specific function calls for creation and/or deletion,
|
|
||||||
# or some of their fields need to be handled manually for now;
|
|
||||||
# making their wrapper constructors/destructors non-trivial.
|
|
||||||
HARDCODED_WRAPPER_STRUCTS: List[str] = [
|
|
||||||
"MjData",
|
|
||||||
"MjModel",
|
|
||||||
"MjvScene",
|
|
||||||
"MjSpec",
|
|
||||||
"MjVisual",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -349,9 +349,9 @@ void MjsElement::set(mjsElement* ptr) {
|
|||||||
),
|
),
|
||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(
|
wrapped_field_data = structs._generate_field_data(
|
||||||
field_with_init, "MjsTexture"
|
field_with_init, "MjsTexture"
|
||||||
).generate()
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_struct_source(
|
structs.build_struct_source(
|
||||||
"mjsTexture",
|
"mjsTexture",
|
||||||
@@ -573,8 +573,7 @@ class StructFieldCodeBuilderTest(absltest.TestCase):
|
|||||||
type=ast_nodes.ValueType(name="int"),
|
type=ast_nodes.ValueType(name="int"),
|
||||||
doc="number of geoms",
|
doc="number of geoms",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(structs._generate_field_data(field, "ngeom").definition,
|
||||||
structs.build_primitive_type_definition(field),
|
|
||||||
"""
|
"""
|
||||||
int ngeom() const {
|
int ngeom() const {
|
||||||
return ptr_->ngeom;
|
return ptr_->ngeom;
|
||||||
@@ -595,9 +594,7 @@ void set_ngeom(int value) {
|
|||||||
array_extent=("ngeom", 4),
|
array_extent=("ngeom", 4),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_memory_view_definition(
|
structs._generate_field_data(field, "geom_rgba").definition,
|
||||||
field, "ptr_->ngeom * 4", "ptr_->geom_rgba"
|
|
||||||
),
|
|
||||||
"""
|
"""
|
||||||
emscripten::val geom_rgba() const {
|
emscripten::val geom_rgba() const {
|
||||||
return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_rgba));
|
return emscripten::val(emscripten::typed_memory_view(ptr_->ngeom * 4, ptr_->geom_rgba));
|
||||||
@@ -614,7 +611,7 @@ emscripten::val geom_rgba() const {
|
|||||||
doc="rgba when material is omitted",
|
doc="rgba when material is omitted",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_string_field_definition(field),
|
structs._generate_field_data(field, "MjString").definition,
|
||||||
"""
|
"""
|
||||||
mjString string_field() const {
|
mjString string_field() const {
|
||||||
return (ptr_ && ptr_->string_field) ? *(ptr_->string_field) : "";
|
return (ptr_ && ptr_->string_field) ? *(ptr_->string_field) : "";
|
||||||
@@ -636,7 +633,7 @@ void set_string_field(const mjString& value) {
|
|||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_mjvec_pointer_definition(field, "mjDoubleVec"),
|
structs._generate_field_data(field, "mjDoubleVec").definition,
|
||||||
"""
|
"""
|
||||||
mjDoubleVec &vector_field() const {
|
mjDoubleVec &vector_field() const {
|
||||||
return *(ptr_->vector_field);
|
return *(ptr_->vector_field);
|
||||||
@@ -652,7 +649,7 @@ mjDoubleVec &vector_field() const {
|
|||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_mjvec_pointer_definition(field, "mjByteVec"),
|
structs._generate_field_data(field, "mjByteVec").definition,
|
||||||
"""
|
"""
|
||||||
std::vector<uint8_t> &vector_field() const {
|
std::vector<uint8_t> &vector_field() const {
|
||||||
return *(reinterpret_cast<std::vector<uint8_t>*>(ptr_->vector_field));
|
return *(reinterpret_cast<std::vector<uint8_t>*>(ptr_->vector_field));
|
||||||
@@ -666,7 +663,7 @@ std::vector<uint8_t> &vector_field() const {
|
|||||||
doc="number of geoms",
|
doc="number of geoms",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_simple_property_binding(field, "MjModel"),
|
structs._simple_property_binding(field, "MjModel"),
|
||||||
'.property("ngeom", &MjModel::ngeom)',
|
'.property("ngeom", &MjModel::ngeom)',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -677,7 +674,7 @@ std::vector<uint8_t> &vector_field() const {
|
|||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_simple_property_binding(field, "MjModel", True),
|
structs._simple_property_binding(field, "MjModel", True),
|
||||||
'.property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom)',
|
'.property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom)',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -688,11 +685,11 @@ std::vector<uint8_t> &vector_field() const {
|
|||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
structs.build_simple_property_binding(
|
structs._simple_property_binding(
|
||||||
field,
|
field,
|
||||||
"MjModel",
|
"MjModel",
|
||||||
add_setter=True,
|
setter=True,
|
||||||
add_return_value_policy_as_ref=True,
|
reference=True,
|
||||||
),
|
),
|
||||||
'.property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom, reference())',
|
'.property("ngeom", &MjModel::ngeom, &MjModel::set_ngeom, reference())',
|
||||||
)
|
)
|
||||||
@@ -708,8 +705,7 @@ class StructFieldHandlerTest(absltest.TestCase):
|
|||||||
doc="number of geoms",
|
doc="number of geoms",
|
||||||
)
|
)
|
||||||
|
|
||||||
field_handler_scalar = structs.StructFieldHandler(field_scalar, "MjModel")
|
wrapped_field_data = structs._generate_field_data(field_scalar, "MjModel")
|
||||||
wrapped_field_data = field_handler_scalar.generate()
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
wrapped_field_data.definition,
|
wrapped_field_data.definition,
|
||||||
"""
|
"""
|
||||||
@@ -736,7 +732,7 @@ void set_ngeom(int value) {
|
|||||||
doc="rgba when material is omitted",
|
doc="rgba when material is omitted",
|
||||||
array_extent=("ngeom", 4),
|
array_extent=("ngeom", 4),
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(field, "MjModel").generate()
|
wrapped_field_data = structs._generate_field_data(field, "MjModel")
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
wrapped_field_data.definition,
|
wrapped_field_data.definition,
|
||||||
@@ -761,7 +757,7 @@ emscripten::val geom_rgba() const {
|
|||||||
),
|
),
|
||||||
doc="main buffer; all pointers point in it (nbuffer bytes)",
|
doc="main buffer; all pointers point in it (nbuffer bytes)",
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(field, "MjData").generate()
|
wrapped_field_data = structs._generate_field_data(field, "MjData")
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
wrapped_field_data.definition,
|
wrapped_field_data.definition,
|
||||||
@@ -782,9 +778,7 @@ emscripten::val buffer() const {
|
|||||||
),
|
),
|
||||||
doc="",
|
doc="",
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(
|
wrapped_field_data = structs._generate_field_data(field, "MjsTexture")
|
||||||
field, "MjsTexture"
|
|
||||||
).generate()
|
|
||||||
self.assertEqual(wrapped_field_data.definition, "MjsElement element;")
|
self.assertEqual(wrapped_field_data.definition, "MjsElement element;")
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -806,9 +800,7 @@ emscripten::val buffer() const {
|
|||||||
),
|
),
|
||||||
doc="gravitational acceleration",
|
doc="gravitational acceleration",
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(
|
wrapped_field_data = structs._generate_field_data(field, "MjOption")
|
||||||
field, "MjOption"
|
|
||||||
).generate()
|
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
wrapped_field_data.definition,
|
wrapped_field_data.definition,
|
||||||
@@ -833,7 +825,7 @@ emscripten::val gravity() const {
|
|||||||
),
|
),
|
||||||
doc="description",
|
doc="description",
|
||||||
)
|
)
|
||||||
wrapped_field_data = structs.StructFieldHandler(field, "MjModel").generate()
|
wrapped_field_data = structs._generate_field_data(field, "MjModel")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
wrapped_field_data.definition,
|
wrapped_field_data.definition,
|
||||||
"""
|
"""
|
||||||
|
|||||||
+173
-319
@@ -49,7 +49,8 @@ class WrappedFieldData:
|
|||||||
# Whether the field is a primitive or fixed size
|
# Whether the field is a primitive or fixed size
|
||||||
is_primitive_or_fixed_size: bool = False
|
is_primitive_or_fixed_size: bool = False
|
||||||
|
|
||||||
# Underlying type of the field
|
# Underlying type of the field. If non-empty, used to determine the order in
|
||||||
|
# which structs are written in the bindings.h file.
|
||||||
typename: str = ""
|
typename: str = ""
|
||||||
|
|
||||||
|
|
||||||
@@ -76,360 +77,217 @@ class WrappedStructData:
|
|||||||
use_shallow_copy: bool = True
|
use_shallow_copy: bool = True
|
||||||
|
|
||||||
|
|
||||||
def build_primitive_type_definition(field: ast_nodes.StructFieldDecl) -> str:
|
def _simple_property_binding(
|
||||||
"""Builds the C++ getter/setter code for a primitive type field wrapper."""
|
|
||||||
if not isinstance(field.type, ast_nodes.ValueType):
|
|
||||||
raise ValueError(f"{field.type} must be ValueType.")
|
|
||||||
|
|
||||||
builder = code_builder.CodeBuilder()
|
|
||||||
with builder.function(f"{field.type.name} {field.name}() const"):
|
|
||||||
builder.line(f"return ptr_->{field.name};")
|
|
||||||
with builder.function(f"void set_{field.name}({field.type.name} value)"):
|
|
||||||
builder.line(f"ptr_->{field.name} = value;")
|
|
||||||
return builder.to_string()
|
|
||||||
|
|
||||||
|
|
||||||
def build_memory_view_definition(
|
|
||||||
field: ast_nodes.StructFieldDecl, array_size_str: str, ptr_expr: str
|
|
||||||
) -> str:
|
|
||||||
"""Builds the C++ code for a pointer type field wrapper."""
|
|
||||||
builder = code_builder.CodeBuilder()
|
|
||||||
with builder.function(f"emscripten::val {field.name}() const"):
|
|
||||||
builder.line(
|
|
||||||
"return"
|
|
||||||
f" emscripten::val(emscripten::typed_memory_view({array_size_str},"
|
|
||||||
f" {ptr_expr}));"
|
|
||||||
)
|
|
||||||
return builder.to_string()
|
|
||||||
|
|
||||||
|
|
||||||
def build_string_field_definition(field: ast_nodes.StructFieldDecl) -> str:
|
|
||||||
"""Builds the C++ code getter/setter for a string type field wrapper."""
|
|
||||||
builder = code_builder.CodeBuilder()
|
|
||||||
with builder.function(f"mjString {field.name}() const"):
|
|
||||||
builder.line(
|
|
||||||
f'return (ptr_ && ptr_->{field.name}) ? *(ptr_->{field.name}) : "";'
|
|
||||||
)
|
|
||||||
with builder.function(f"void set_{field.name}(const mjString& value)"):
|
|
||||||
with builder.block(f"if (ptr_ && ptr_->{field.name})"):
|
|
||||||
builder.line(f"*(ptr_->{field.name}) = value;")
|
|
||||||
return builder.to_string()
|
|
||||||
|
|
||||||
|
|
||||||
def build_mjvec_pointer_definition(
|
|
||||||
field: ast_nodes.StructFieldDecl, vector_type: str
|
|
||||||
) -> str:
|
|
||||||
"""Builds the C++ code for a mjVec type field wrapper."""
|
|
||||||
ptr_field_expr = f"*(ptr_->{field.name})"
|
|
||||||
if vector_type == "mjByteVec":
|
|
||||||
vector_type = "std::vector<uint8_t>"
|
|
||||||
ptr_field_expr = (
|
|
||||||
f"*(reinterpret_cast<std::vector<uint8_t>*>(ptr_->{field.name}))"
|
|
||||||
)
|
|
||||||
builder = code_builder.CodeBuilder()
|
|
||||||
with builder.function(f"{vector_type} &{field.name}() const"):
|
|
||||||
builder.line(f"return {ptr_field_expr};")
|
|
||||||
return builder.to_string()
|
|
||||||
|
|
||||||
|
|
||||||
def build_simple_property_binding(
|
|
||||||
field: ast_nodes.StructFieldDecl,
|
field: ast_nodes.StructFieldDecl,
|
||||||
struct_wrapper_name: str,
|
struct_wrapper_name: str,
|
||||||
add_setter: bool = False,
|
setter: bool = False,
|
||||||
add_return_value_policy_as_ref: bool = False,
|
reference: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Builds the C++ code for a simple property binding."""
|
"""Builds the C++ code for a simple property binding."""
|
||||||
builder = code_builder.CodeBuilder()
|
f = field
|
||||||
setter_txt = ""
|
w = struct_wrapper_name
|
||||||
if add_setter:
|
setter_txt = f", &{w}::set_{f.name}" if setter else ""
|
||||||
setter_txt = f", &{struct_wrapper_name}::set_{field.name}"
|
reference_txt = ", reference()" if reference else ""
|
||||||
if add_return_value_policy_as_ref:
|
return f'.property("{f.name}", &{w}::{f.name}{setter_txt}{reference_txt})'
|
||||||
as_reference_txt = ", reference()"
|
|
||||||
else:
|
|
||||||
as_reference_txt = ""
|
|
||||||
builder.line(
|
|
||||||
f'.property("{field.name}",'
|
|
||||||
f" &{struct_wrapper_name}::{field.name}{setter_txt}{as_reference_txt})"
|
|
||||||
)
|
|
||||||
return builder.to_string()
|
|
||||||
|
|
||||||
|
|
||||||
class StructFieldHandler:
|
def _generate_field_data(
|
||||||
"""Class to handle the different struct field types, and provide the c++ code for the definitions and bindings."""
|
field: ast_nodes.StructFieldDecl, struct_wrapper_name: str
|
||||||
|
) -> WrappedFieldData:
|
||||||
|
"""Generates the C++ definition and binding code for the struct field."""
|
||||||
|
f = field
|
||||||
|
w = struct_wrapper_name
|
||||||
|
s = common.lowercase_first_letter(w)
|
||||||
|
|
||||||
def __init__(
|
if f.name in constants.MANUAL_FIELDS.get(w, []):
|
||||||
self,
|
# Note: Manually handled MjModel fields are special cased so that a
|
||||||
field: ast_nodes.StructFieldDecl,
|
# by-reference embind return value policy is used.
|
||||||
struct_wrapper_name: str,
|
|
||||||
):
|
|
||||||
self.field = field
|
|
||||||
self.struct_wrapper_name = struct_wrapper_name
|
|
||||||
self.simple_property_binding = build_simple_property_binding(
|
|
||||||
self.field, self.struct_wrapper_name
|
|
||||||
)
|
|
||||||
self.manually_added_fields = (
|
|
||||||
constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.get(
|
|
||||||
self.struct_wrapper_name, {}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def generate(self) -> WrappedFieldData:
|
|
||||||
"""Generates the C++ definition and binding code for the struct field."""
|
|
||||||
field_type = self.field.type
|
|
||||||
if isinstance(field_type, ast_nodes.ValueType) and (
|
|
||||||
field_type.name in constants.PRIMITIVE_TYPES
|
|
||||||
or field_type.name.startswith("mjt")
|
|
||||||
):
|
|
||||||
return self._handle_primitive()
|
|
||||||
elif isinstance(field_type, ast_nodes.PointerType):
|
|
||||||
return self._handle_pointer()
|
|
||||||
elif isinstance(field_type, ast_nodes.ArrayType):
|
|
||||||
return self._handle_array()
|
|
||||||
elif isinstance(
|
|
||||||
field_type, ast_nodes.ValueType
|
|
||||||
) and field_type.name.startswith("mj"):
|
|
||||||
return self._handle_mj_struct()
|
|
||||||
elif isinstance(field_type, ast_nodes.AnonymousStructDecl):
|
|
||||||
return self._handle_anonymous_struct()
|
|
||||||
return self._undefined()
|
|
||||||
|
|
||||||
def _handle_primitive(self) -> WrappedFieldData:
|
|
||||||
"""Handles the generation of C++ definition and binding code for primitive fields."""
|
|
||||||
return WrappedFieldData(
|
return WrappedFieldData(
|
||||||
definition=(build_primitive_type_definition(self.field)),
|
typename=_get_field_struct_type(f.type),
|
||||||
typename=_get_field_struct_type(self.field.type),
|
definition=f"// {f.name} field is handled manually in template file struct declaration", # pylint: disable=line-too-long
|
||||||
binding=build_simple_property_binding(
|
binding=_simple_property_binding(f, w, reference=(w == "MjModel")),
|
||||||
self.field,
|
)
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=True,
|
if f.name in constants.SKIPPED_FIELDS.get(w, []):
|
||||||
add_return_value_policy_as_ref=True,
|
return WrappedFieldData(
|
||||||
),
|
typename="",
|
||||||
|
definition=f"// {f.name} field is skipped.",
|
||||||
|
binding=f"// {f.name} field is skipped.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(f.type, ast_nodes.ValueType) and (
|
||||||
|
f.type.name in constants.PRIMITIVE_TYPES or f.type.name.startswith("mjt")
|
||||||
|
):
|
||||||
|
|
||||||
|
builder = code_builder.CodeBuilder()
|
||||||
|
with builder.function(f"{f.type.name} {f.name}() const"):
|
||||||
|
builder.line(f"return ptr_->{f.name};")
|
||||||
|
with builder.function(f"void set_{f.name}({f.type.name} value)"):
|
||||||
|
builder.line(f"ptr_->{f.name} = value;")
|
||||||
|
|
||||||
|
return WrappedFieldData(
|
||||||
|
definition=builder.to_string(),
|
||||||
|
typename=_get_field_struct_type(f.type),
|
||||||
|
binding=_simple_property_binding(f, w, setter=True, reference=True),
|
||||||
is_primitive_or_fixed_size=True,
|
is_primitive_or_fixed_size=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _handle_pointer(self) -> WrappedFieldData:
|
elif isinstance(f.type, ast_nodes.ValueType) and f.type.name.startswith("mj"):
|
||||||
"""Handles the generation of C++ definition and binding code for pointer fields."""
|
|
||||||
if not isinstance(self.field.type, ast_nodes.PointerType):
|
return WrappedFieldData(
|
||||||
raise ValueError(
|
definition=f"{common.uppercase_first_letter(f.type.name)} {f.name};",
|
||||||
f"Expected PointerType, got {type(self.field.type)} for field"
|
typename=_get_field_struct_type(f.type),
|
||||||
f" {self.field.name}"
|
binding=_simple_property_binding(f, w, setter=False, reference=True),
|
||||||
|
initialization=f", {f.name}(&ptr_->{f.name})",
|
||||||
|
ptr_copy_reset=f"{f.name}.set(&ptr_->{f.name});",
|
||||||
|
is_primitive_or_fixed_size=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif isinstance(f.type, ast_nodes.AnonymousStructDecl):
|
||||||
|
|
||||||
|
anonymous_struct_name = ""
|
||||||
|
for name, value in constants.ANONYMOUS_STRUCTS.items():
|
||||||
|
if (value["parent"] == s and value["field_name"] == f.name):
|
||||||
|
anonymous_struct_name = name
|
||||||
|
break
|
||||||
|
|
||||||
|
if anonymous_struct_name in constants.STRUCTS_TO_BIND:
|
||||||
|
return WrappedFieldData(
|
||||||
|
binding=_simple_property_binding(f, w, setter=False, reference=True),
|
||||||
|
typename=_get_field_struct_type(f.type),
|
||||||
|
initialization=f", {f.name}(&ptr_->{f.name})",
|
||||||
|
ptr_copy_reset=f"{f.name}.set(&ptr_->{f.name});",
|
||||||
|
is_primitive_or_fixed_size=True,
|
||||||
)
|
)
|
||||||
field_type: ast_nodes.PointerType = self.field.type
|
|
||||||
|
elif isinstance(f.type, ast_nodes.ArrayType):
|
||||||
|
|
||||||
|
inner_type = f.type.inner_type
|
||||||
|
size = math.prod(f.type.extents)
|
||||||
|
|
||||||
|
if (
|
||||||
|
isinstance(inner_type, ast_nodes.ValueType)
|
||||||
|
and inner_type.name in constants.PRIMITIVE_TYPES
|
||||||
|
):
|
||||||
|
ptr_expr = f"ptr_->{f.name}"
|
||||||
|
if len(f.type.extents) > 1:
|
||||||
|
# for multi-dimensional arrays, we need to cast the field
|
||||||
|
# to a pointer, so embind can correctly interpret the memory
|
||||||
|
# view
|
||||||
|
ptr_expr = f"reinterpret_cast<{inner_type.name}*>({ptr_expr})"
|
||||||
|
|
||||||
|
builder = code_builder.CodeBuilder()
|
||||||
|
with builder.function(f"emscripten::val {f.name}() const"):
|
||||||
|
builder.line(
|
||||||
|
"return"
|
||||||
|
f" emscripten::val(emscripten::typed_memory_view({str(size)},"
|
||||||
|
f" {ptr_expr}));"
|
||||||
|
)
|
||||||
|
|
||||||
|
return WrappedFieldData(
|
||||||
|
definition=builder.to_string(),
|
||||||
|
typename=_get_field_struct_type(f.type),
|
||||||
|
binding=_simple_property_binding(f, w),
|
||||||
|
is_primitive_or_fixed_size=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return WrappedFieldData(
|
||||||
|
definition=f"// TODO: NOT IMPLEMENTED ARRAY wrapper for {f.name}",
|
||||||
|
typename=_get_field_struct_type(f.type),
|
||||||
|
binding=f"// TODO: NOT IMPLEMENTED ARRAY binding for {f.name}",
|
||||||
|
)
|
||||||
|
|
||||||
|
elif isinstance(f.type, ast_nodes.PointerType):
|
||||||
|
|
||||||
inner_type_name = (
|
inner_type_name = (
|
||||||
field_type.inner_type.name
|
f.type.inner_type.name
|
||||||
if isinstance(field_type.inner_type, ast_nodes.ValueType)
|
if isinstance(f.type.inner_type, ast_nodes.ValueType)
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
ptr_field_expr = f"ptr_->{self.field.name}"
|
ptr_field_expr = f"ptr_->{f.name}"
|
||||||
array_size_str = ""
|
array_size_str = ""
|
||||||
|
|
||||||
if self.field.array_extent:
|
if f.array_extent:
|
||||||
array_size_str = parse_array_extent(
|
array_size_str = parse_array_extent(f.array_extent, w, f.name)
|
||||||
self.field.array_extent, self.struct_wrapper_name, self.field.name
|
elif f.name in constants.BYTE_FIELDS.keys():
|
||||||
)
|
|
||||||
elif self.field.name in constants.BYTE_FIELDS.keys():
|
|
||||||
# for byte fields, we need to cast the pointer to uint8_t*
|
# for byte fields, we need to cast the pointer to uint8_t*
|
||||||
# so embind can correctly interpret the memory view
|
# so embind can correctly interpret the memory view
|
||||||
ptr_field_expr = f"static_cast<uint8_t*>({ptr_field_expr})"
|
ptr_field_expr = f"static_cast<uint8_t*>({ptr_field_expr})"
|
||||||
# for these byte fields, there is no array_extent, so we add the size of
|
# for these byte fields, there is no array_extent, so we add the size of
|
||||||
# in the config file based in the documentation
|
# in the config file based in the documentation
|
||||||
extent = (constants.BYTE_FIELDS[self.field.name]["size"],)
|
extent = (constants.BYTE_FIELDS[f.name]["size"],)
|
||||||
array_size_str = parse_array_extent(
|
array_size_str = parse_array_extent(extent, w, f.name)
|
||||||
extent, self.struct_wrapper_name, self.field.name
|
|
||||||
)
|
|
||||||
elif inner_type_name == "mjString":
|
elif inner_type_name == "mjString":
|
||||||
|
|
||||||
|
builder = code_builder.CodeBuilder()
|
||||||
|
with builder.function(f"mjString {f.name}() const"):
|
||||||
|
builder.line(
|
||||||
|
f'return (ptr_ && ptr_->{f.name}) ? *(ptr_->{f.name}) : "";'
|
||||||
|
)
|
||||||
|
with builder.function(f"void set_{f.name}(const mjString& value)"):
|
||||||
|
with builder.block(f"if (ptr_ && ptr_->{f.name})"):
|
||||||
|
builder.line(f"*(ptr_->{f.name}) = value;")
|
||||||
|
|
||||||
return WrappedFieldData(
|
return WrappedFieldData(
|
||||||
definition=build_string_field_definition(self.field),
|
definition=builder.to_string(),
|
||||||
typename=_get_field_struct_type(self.field.type),
|
typename=_get_field_struct_type(f.type),
|
||||||
binding=build_simple_property_binding(
|
binding=_simple_property_binding(f, w, setter=True, reference=True),
|
||||||
self.field,
|
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=True,
|
|
||||||
add_return_value_policy_as_ref=True,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
elif inner_type_name.startswith("mj") and inner_type_name.endswith("Vec"):
|
elif inner_type_name.startswith("mj") and inner_type_name.endswith("Vec"):
|
||||||
|
ptr_field_expr_vec = f"*(ptr_->{f.name})"
|
||||||
|
vector_type = inner_type_name
|
||||||
|
if vector_type == "mjByteVec":
|
||||||
|
vector_type = "std::vector<uint8_t>"
|
||||||
|
ptr_field_expr_vec = (
|
||||||
|
f"*(reinterpret_cast<std::vector<uint8_t>*>(ptr_->{f.name}))"
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = code_builder.CodeBuilder()
|
||||||
|
with builder.function(f"{vector_type} &{f.name}() const"):
|
||||||
|
builder.line(f"return {ptr_field_expr_vec};")
|
||||||
|
|
||||||
return WrappedFieldData(
|
return WrappedFieldData(
|
||||||
definition=build_mjvec_pointer_definition(
|
definition=builder.to_string(),
|
||||||
self.field, inner_type_name
|
typename=_get_field_struct_type(f.type),
|
||||||
),
|
binding=_simple_property_binding(f, w, setter=False, reference=True)
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
binding=build_simple_property_binding(
|
|
||||||
self.field,
|
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=False,
|
|
||||||
add_return_value_policy_as_ref=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
elif inner_type_name in constants.PRIMITIVE_TYPES:
|
|
||||||
return self._get_manual_definition(
|
|
||||||
comment_type="primitive pointer field with complex extents"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
inner_type_name.startswith("mj")
|
inner_type_name.startswith("mj")
|
||||||
and inner_type_name not in constants.PRIMITIVE_TYPES
|
and inner_type_name not in constants.PRIMITIVE_TYPES
|
||||||
|
and not f.array_extent
|
||||||
|
and w not in constants.MANUAL_FIELDS.keys()
|
||||||
):
|
):
|
||||||
# it's a pointer to a single struct,
|
ptr_field = cast(ast_nodes.PointerType, f.type)
|
||||||
# like the `element` field in mjs structs
|
wrapper_field_name = common.uppercase_first_letter(
|
||||||
# and the struct is not manually added
|
cast(ast_nodes.ValueType, ptr_field.inner_type).name
|
||||||
if (
|
|
||||||
not self.field.array_extent
|
|
||||||
and self.struct_wrapper_name
|
|
||||||
not in constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.keys()
|
|
||||||
):
|
|
||||||
ptr_field = cast(ast_nodes.PointerType, self.field.type)
|
|
||||||
wrapper_field_name = common.uppercase_first_letter(
|
|
||||||
cast(ast_nodes.ValueType, ptr_field.inner_type).name
|
|
||||||
)
|
|
||||||
return WrappedFieldData(
|
|
||||||
definition=f"{wrapper_field_name} {self.field.name};",
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
binding=build_simple_property_binding(
|
|
||||||
self.field,
|
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=False,
|
|
||||||
add_return_value_policy_as_ref=True,
|
|
||||||
),
|
|
||||||
initialization=f", {self.field.name}(ptr_->{self.field.name})",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return self._get_manual_definition(comment_type="complex pointer field")
|
|
||||||
|
|
||||||
return WrappedFieldData(
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
definition=(
|
|
||||||
build_memory_view_definition(
|
|
||||||
self.field, array_size_str, ptr_field_expr
|
|
||||||
)
|
|
||||||
),
|
|
||||||
binding=self.simple_property_binding,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _handle_array(self) -> WrappedFieldData:
|
|
||||||
"""Handles the generation of C++ definition and binding code for array fields."""
|
|
||||||
field_type = self.field.type
|
|
||||||
if not isinstance(field_type, ast_nodes.ArrayType):
|
|
||||||
raise ValueError(
|
|
||||||
f"Expected ArrayType, got {type(field_type)} for field"
|
|
||||||
f" {self.field.name}"
|
|
||||||
)
|
)
|
||||||
inner_type = field_type.inner_type
|
|
||||||
size = math.prod(field_type.extents)
|
|
||||||
|
|
||||||
if isinstance(inner_type, ast_nodes.ValueType):
|
|
||||||
if inner_type.name in constants.PRIMITIVE_TYPES:
|
|
||||||
ptr_expr = f"ptr_->{self.field.name}"
|
|
||||||
if len(field_type.extents) > 1:
|
|
||||||
# for multi-dimensional arrays, we need to cast the field
|
|
||||||
# to a pointer, so embind can correctly interpret the memory
|
|
||||||
# view
|
|
||||||
ptr_expr = f"reinterpret_cast<{inner_type.name}*>({ptr_expr})"
|
|
||||||
return WrappedFieldData(
|
|
||||||
definition=(
|
|
||||||
build_memory_view_definition(self.field, str(size), ptr_expr)
|
|
||||||
),
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
binding=self.simple_property_binding,
|
|
||||||
is_primitive_or_fixed_size=True,
|
|
||||||
)
|
|
||||||
elif inner_type.name.startswith("mj") and not inner_type.name.startswith(
|
|
||||||
"mjt"
|
|
||||||
):
|
|
||||||
return self._get_manual_definition(comment_type="array field")
|
|
||||||
|
|
||||||
return WrappedFieldData(
|
|
||||||
definition=(
|
|
||||||
f"// TODO: NOT IMPLEMENTED ARRAY wrapper for {self.field.name}"
|
|
||||||
),
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
binding=f"// TODO: NOT IMPLEMENTED ARRAY binding for {self.field.name}",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _handle_mj_struct(self) -> WrappedFieldData:
|
|
||||||
"""Handles the generation of C++ definition and binding code for mj struct fields."""
|
|
||||||
if (
|
|
||||||
isinstance(self.field.type, ast_nodes.ValueType)
|
|
||||||
and self.field.name not in self.manually_added_fields
|
|
||||||
and self.field.type.name in constants.STRUCTS_TO_BIND
|
|
||||||
):
|
|
||||||
# TODO(manevi): Find a better way to do this instead of checking the
|
|
||||||
# struct wrapper name.
|
|
||||||
definition = ""
|
|
||||||
if self.struct_wrapper_name not in constants.HARDCODED_WRAPPER_STRUCTS:
|
|
||||||
wrapper_field_name = common.uppercase_first_letter(self.field.type.name)
|
|
||||||
definition = f"{wrapper_field_name} {self.field.name};"
|
|
||||||
return WrappedFieldData(
|
return WrappedFieldData(
|
||||||
definition=definition,
|
definition=f"{wrapper_field_name} {f.name};",
|
||||||
typename=_get_field_struct_type(self.field.type),
|
typename=_get_field_struct_type(f.type),
|
||||||
binding=build_simple_property_binding(
|
binding=_simple_property_binding(f, w, setter=False, reference=True),
|
||||||
self.field,
|
initialization=f", {f.name}(ptr_->{f.name})",
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=False,
|
|
||||||
add_return_value_policy_as_ref=True,
|
|
||||||
),
|
|
||||||
initialization=f", {self.field.name}(&ptr_->{self.field.name})",
|
|
||||||
ptr_copy_reset=f"{self.field.name}.set(&ptr_->{self.field.name});",
|
|
||||||
is_primitive_or_fixed_size=True,
|
|
||||||
)
|
)
|
||||||
return self._get_manual_definition(comment_type="struct field")
|
|
||||||
|
|
||||||
def _handle_anonymous_struct(self) -> WrappedFieldData:
|
builder = code_builder.CodeBuilder()
|
||||||
"""Handles the generation of C++ definition and binding code for anonymous struct fields."""
|
with builder.function(f"emscripten::val {f.name}() const"):
|
||||||
|
builder.line(
|
||||||
anonymous_struct_name = ""
|
"return"
|
||||||
for name, value in constants.ANONYMOUS_STRUCTS.items():
|
f" emscripten::val(emscripten::typed_memory_view({array_size_str},"
|
||||||
if (
|
f" {ptr_field_expr}));"
|
||||||
common.uppercase_first_letter(value["parent"])
|
|
||||||
== self.struct_wrapper_name
|
|
||||||
and value["field_name"] == self.field.name
|
|
||||||
):
|
|
||||||
anonymous_struct_name = name
|
|
||||||
break
|
|
||||||
|
|
||||||
if (
|
|
||||||
isinstance(self.field.type, ast_nodes.AnonymousStructDecl)
|
|
||||||
and self.field.name not in self.manually_added_fields
|
|
||||||
and anonymous_struct_name in constants.STRUCTS_TO_BIND
|
|
||||||
):
|
|
||||||
return WrappedFieldData(
|
|
||||||
binding=build_simple_property_binding(
|
|
||||||
self.field,
|
|
||||||
self.struct_wrapper_name,
|
|
||||||
add_setter=False,
|
|
||||||
add_return_value_policy_as_ref=True,
|
|
||||||
),
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
initialization=f", {self.field.name}(&ptr_->{self.field.name})",
|
|
||||||
ptr_copy_reset=f"{self.field.name}.set(&ptr_->{self.field.name});",
|
|
||||||
is_primitive_or_fixed_size=True,
|
|
||||||
)
|
)
|
||||||
return self._get_manual_definition(comment_type="anonymous struct field")
|
|
||||||
|
|
||||||
def _undefined(self) -> WrappedFieldData:
|
|
||||||
"""This function adds a TODO comment for fields that are not handled by this class yet."""
|
|
||||||
return WrappedFieldData(
|
return WrappedFieldData(
|
||||||
definition=f"// TODO: UNDEFINED definition for {self.field.name}",
|
definition=builder.to_string(),
|
||||||
binding=f"// TODO: UNDEFINED binding for {self.field.name}",
|
typename=_get_field_struct_type(f.type),
|
||||||
|
binding=_simple_property_binding(f, w),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_manual_definition(self, comment_type: str = "") -> WrappedFieldData:
|
return WrappedFieldData(
|
||||||
"""Helper method to generate a comment as a definition for manually added fields."""
|
definition=f"// TODO: UNDEFINED definition for {f.name}",
|
||||||
if self.field.name in self.manually_added_fields:
|
typename=_get_field_struct_type(f.type),
|
||||||
return WrappedFieldData(
|
binding=f"// TODO: UNDEFINED binding for {f.name}",
|
||||||
typename=_get_field_struct_type(self.field.type),
|
)
|
||||||
definition=(
|
|
||||||
f"// {comment_type} is defined manually. {self.field.name}"
|
|
||||||
),
|
|
||||||
binding=self.simple_property_binding,
|
|
||||||
)
|
|
||||||
return WrappedFieldData(
|
|
||||||
typename=_get_field_struct_type(self.field.type),
|
|
||||||
definition=(
|
|
||||||
f"// TODO: Define {comment_type} manually for {self.field.name}"
|
|
||||||
),
|
|
||||||
binding=f"// TODO: {self.simple_property_binding}",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _has_nested_wrapper_members(struct_info: ast_nodes.StructDecl) -> bool:
|
def _has_nested_wrapper_members(struct_info: ast_nodes.StructDecl) -> bool:
|
||||||
@@ -502,10 +360,6 @@ def _default_function_statement(struct_name: str) -> str:
|
|||||||
if (
|
if (
|
||||||
struct_name in constants.ANONYMOUS_STRUCTS.keys()
|
struct_name in constants.ANONYMOUS_STRUCTS.keys()
|
||||||
or struct_name in constants.NO_DEFAULT_CONSTRUCTORS
|
or struct_name in constants.NO_DEFAULT_CONSTRUCTORS
|
||||||
or (
|
|
||||||
common.uppercase_first_letter(struct_name)
|
|
||||||
in constants.MANUALLY_ADDED_FIELDS_FROM_TEMPLATE.keys()
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
return ""
|
return ""
|
||||||
elif struct_name.startswith("mjs"):
|
elif struct_name.startswith("mjs"):
|
||||||
@@ -568,7 +422,7 @@ def build_struct_header(
|
|||||||
is_anonymous_struct = struct_name in constants.ANONYMOUS_STRUCTS.keys()
|
is_anonymous_struct = struct_name in constants.ANONYMOUS_STRUCTS.keys()
|
||||||
is_hardcoded_wrapper_struct = (
|
is_hardcoded_wrapper_struct = (
|
||||||
common.uppercase_first_letter(struct_name)
|
common.uppercase_first_letter(struct_name)
|
||||||
in constants.HARDCODED_WRAPPER_STRUCTS
|
in constants.MANUAL_STRUCTS
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -765,7 +619,7 @@ def generate_wasm_bindings(
|
|||||||
|
|
||||||
wrapped_fields: List[WrappedFieldData] = []
|
wrapped_fields: List[WrappedFieldData] = []
|
||||||
for field in struct_fields:
|
for field in struct_fields:
|
||||||
wrapped_field = StructFieldHandler(field, wrapped_name).generate()
|
wrapped_field = _generate_field_data(field, wrapped_name)
|
||||||
wrapped_fields.append(wrapped_field)
|
wrapped_fields.append(wrapped_field)
|
||||||
|
|
||||||
wrapped_header = build_struct_header(
|
wrapped_header = build_struct_header(
|
||||||
|
|||||||
Reference in New Issue
Block a user