Check mjs_getName return value in XML parser. Fixes #2898.

PiperOrigin-RevId: 819351410
Change-Id: I0807d71328068f6388991ae15274c7d61b4d3e53
This commit is contained in:
Alessio Quaglino
2025-10-14 12:38:51 -07:00
committed by Copybara-Service
parent 67960543a0
commit b6f25ca623
11 changed files with 152 additions and 63 deletions
+2 -1
View File
@@ -18,11 +18,12 @@ include(CheckCSourceCompiles)
# linker. # linker.
function(get_mujoco_extra_link_options OUTPUT_VAR) function(get_mujoco_extra_link_options OUTPUT_VAR)
if(MSVC) if(MSVC)
set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5) set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5 /STACK:16777216)
else() else()
set(EXTRA_LINK_OPTIONS) set(EXTRA_LINK_OPTIONS)
if(WIN32) if(WIN32)
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -Wl,/STACK:16777216)
set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link") set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link")
check_c_source_compiles("int main() {}" SUPPORTS_LLD) check_c_source_compiles("int main() {}" SUPPORTS_LLD)
if(SUPPORTS_LLD) if(SUPPORTS_LLD)
+9
View File
@@ -2,6 +2,15 @@
Changelog Changelog
========= =========
Upcoming version (not yet released)
-----------------------------------
General
^^^^^^^^^
- Raise an error if there are name collisions also during parsing.
- Increase Windows stack size to 16MB to enable models with deep nested body hierarchies.
Version 3.3.7 (October 13, 2025) Version 3.3.7 (October 13, 2025)
----------------------------------- -----------------------------------
+2 -1
View File
@@ -18,11 +18,12 @@ include(CheckCSourceCompiles)
# linker. # linker.
function(get_mujoco_extra_link_options OUTPUT_VAR) function(get_mujoco_extra_link_options OUTPUT_VAR)
if(MSVC) if(MSVC)
set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5) set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5 /STACK:16777216)
else() else()
set(EXTRA_LINK_OPTIONS) set(EXTRA_LINK_OPTIONS)
if(WIN32) if(WIN32)
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -Wl,/STACK:16777216)
set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link") set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link")
check_c_source_compiles("int main() {}" SUPPORTS_LLD) check_c_source_compiles("int main() {}" SUPPORTS_LLD)
if(SUPPORTS_LLD) if(SUPPORTS_LLD)
+2 -1
View File
@@ -18,11 +18,12 @@ include(CheckCSourceCompiles)
# linker. # linker.
function(get_mujoco_extra_link_options OUTPUT_VAR) function(get_mujoco_extra_link_options OUTPUT_VAR)
if(MSVC) if(MSVC)
set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5) set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5 /STACK:16777216)
else() else()
set(EXTRA_LINK_OPTIONS) set(EXTRA_LINK_OPTIONS)
if(WIN32) if(WIN32)
set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -Wl,/STACK:16777216)
set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link") set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link")
check_c_source_compiles("int main() {}" SUPPORTS_LLD) check_c_source_compiles("int main() {}" SUPPORTS_LLD)
if(SUPPORTS_LLD) if(SUPPORTS_LLD)
+4
View File
@@ -279,6 +279,10 @@ mjsElement* mjs_attach(mjsElement* parent, const mjsElement* child,
// get error message from model // get error message from model
const char* mjs_getError(mjSpec* s) { const char* mjs_getError(mjSpec* s) {
if (!s) {
mju_error("spec is null");
return nullptr;
}
mjCModel* modelC = static_cast<mjCModel*>(s->element); mjCModel* modelC = static_cast<mjCModel*>(s->element);
return modelC->GetError().message; return modelC->GetError().message;
} }
+50 -29
View File
@@ -27,6 +27,7 @@
#include <filesystem> // NOLINT(build/c++17) #include <filesystem> // NOLINT(build/c++17)
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <thread> #include <thread>
@@ -4999,78 +5000,98 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
std::string mjCModel::PrintTree(const mjCBody* body, std::string indent) { static void PrintIndent(std::stringstream& ss, int depth) {
std::string tree; // A static string of spaces, created only once during the program's lifetime.
tree += indent + "<body>\n"; static const std::string spaces(1024, ' ');
indent += " ";
if (depth > 0) {
// Write 'depth * 2' spaces directly to the stringstream
// without creating any new std::string objects.
ss.write(spaces.c_str(), std::min((size_t)depth * 2, spaces.length()));
}
}
void mjCModel::PrintTree(std::stringstream& tree, const mjCBody* body, int depth) {
if (depth == 1024) {
throw mjCError(body, "depth limit exceeded in signature computation");
}
PrintIndent(tree, depth);
tree << "<body>\n";
for (const auto& joint : body->joints) { for (const auto& joint : body->joints) {
tree += indent + "<joint>" + std::to_string(joint->nq()) + "</joint>\n"; PrintIndent(tree, depth + 1);
tree << "<joint>" << std::to_string(joint->nq()) << "</joint>\n";
} }
for (uint64_t i = 0; i < body->geoms.size(); ++i) { for (uint64_t i = 0; i < body->geoms.size(); ++i) {
tree += indent + "<geom/>\n"; PrintIndent(tree, depth + 1);
tree << "<geom/>\n";
} }
for (uint64_t i = 0; i < body->sites.size(); ++i) { for (uint64_t i = 0; i < body->sites.size(); ++i) {
tree += indent + "<site/>\n"; PrintIndent(tree, depth + 1);
tree << "<site/>\n";
} }
for (uint64_t i = 0; i < body->cameras.size(); ++i) { for (uint64_t i = 0; i < body->cameras.size(); ++i) {
tree += indent + "<camera/>\n"; PrintIndent(tree, depth + 1);
tree << "<camera/>\n";
} }
for (uint64_t i = 0; i < body->lights.size(); ++i) { for (uint64_t i = 0; i < body->lights.size(); ++i) {
tree += indent + "<light/>\n"; PrintIndent(tree, depth + 1);
tree << "<light/>\n";
} }
for (uint64_t i = 0; i < body->bodies.size(); ++i) { for (uint64_t i = 0; i < body->bodies.size(); ++i) {
tree += PrintTree(body->bodies[i], indent); PrintTree(tree, body->bodies[i], depth + 1);
} }
indent.pop_back(); PrintIndent(tree, depth);
indent.pop_back(); tree << "</body>\n";
tree += indent + "</body>\n";
return tree;
} }
uint64_t mjCModel::Signature() { uint64_t mjCModel::Signature() {
std::string tree = "\n" + PrintTree(bodies_[0]); std::stringstream tree;
tree << "\n";
PrintTree(tree, bodies_[0]);
for (unsigned int i = 0; i < flexes_.size(); ++i) { for (unsigned int i = 0; i < flexes_.size(); ++i) {
tree += "<flex/>\n"; tree << "<flex/>\n";
} }
for (unsigned int i = 0; i < meshes_.size(); ++i) { for (unsigned int i = 0; i < meshes_.size(); ++i) {
tree += "<mesh/>\n"; tree << "<mesh/>\n";
} }
for (unsigned int i = 0; i < skins_.size(); ++i) { for (unsigned int i = 0; i < skins_.size(); ++i) {
tree += "<skin/>\n"; tree << "<skin/>\n";
} }
for (unsigned int i = 0; i < hfields_.size(); ++i) { for (unsigned int i = 0; i < hfields_.size(); ++i) {
tree += "<heightfield/>\n"; tree << "<heightfield/>\n";
} }
for (unsigned int i = 0; i < textures_.size(); ++i) { for (unsigned int i = 0; i < textures_.size(); ++i) {
tree += "<texture/>\n"; tree << "<texture/>\n";
} }
for (unsigned int i = 0; i < materials_.size(); ++i) { for (unsigned int i = 0; i < materials_.size(); ++i) {
tree += "<material/>\n"; tree << "<material/>\n";
} }
for (unsigned int i = 0; i < pairs_.size(); ++i) { for (unsigned int i = 0; i < pairs_.size(); ++i) {
tree += "<pair/>\n"; tree << "<pair/>\n";
} }
for (unsigned int i = 0; i < excludes_.size(); ++i) { for (unsigned int i = 0; i < excludes_.size(); ++i) {
tree += "<exclude/>\n"; tree << "<exclude/>\n";
} }
for (unsigned int i = 1; i < equalities_.size(); ++i) { for (unsigned int i = 1; i < equalities_.size(); ++i) {
tree += "<equality/>\n"; tree << "<equality/>\n";
} }
for (unsigned int i = 0; i < tendons_.size(); ++i) { for (unsigned int i = 0; i < tendons_.size(); ++i) {
tree += "<tendon/>\n"; tree << "<tendon/>\n";
} }
for (unsigned int i = 0; i < actuators_.size(); ++i) { for (unsigned int i = 0; i < actuators_.size(); ++i) {
tree += "<actuator/>\n"; tree << "<actuator/>\n";
} }
for (unsigned int i = 0; i < sensors_.size(); ++i) { for (unsigned int i = 0; i < sensors_.size(); ++i) {
tree += "<sensor>" + std::to_string(sensors_[i]->spec.type) + "<sensor/>\n"; tree << "<sensor>" << std::to_string(sensors_[i]->spec.type) << "<sensor/>\n";
} }
for (unsigned int i = 0; i < keys_.size(); ++i) { for (unsigned int i = 0; i < keys_.size(); ++i) {
tree += "<key/>\n"; tree << "<key/>\n";
} }
return mj_hashString(tree.c_str(), UINT64_MAX); return mj_hashString(tree.str().c_str(), UINT64_MAX);
} }
+2 -1
View File
@@ -19,6 +19,7 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <map> #include <map>
#include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <unordered_map> #include <unordered_map>
@@ -456,7 +457,7 @@ class mjCModel : public mjCModel_, private mjSpec {
const std::vector<T*>& list); const std::vector<T*>& list);
// print the tree of a body // print the tree of a body
std::string PrintTree(const mjCBody* body, std::string indent = ""); void PrintTree(std::stringstream& tree, const mjCBody* body, int depth = 0);
// generate a signature for the model // generate a signature for the model
uint64_t Signature(); uint64_t Signature();
+77 -29
View File
@@ -1401,7 +1401,9 @@ void mjXReader::OneFlex(XMLElement* elem, mjsFlex* flex) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(flex->element, name.c_str()); if (mjs_setName(flex->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "material", material)) { if (ReadAttrTxt(elem, "material", material)) {
mjs_setString(flex->material, material.c_str()); mjs_setString(flex->material, material.c_str());
@@ -1495,7 +1497,9 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* mesh, const mjVFS* vfs) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(mesh->element, name.c_str()); if (mjs_setName(mesh->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "content_type", content_type)) { if (ReadAttrTxt(elem, "content_type", content_type)) {
*mesh->content_type = content_type; *mesh->content_type = content_type;
@@ -1568,7 +1572,7 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* mesh, const mjVFS* vfs) {
throw mjXError(elem, "builtin mesh cannot be used with user vertex data"); throw mjXError(elem, "builtin mesh cannot be used with user vertex data");
} }
if (mjs_makeMesh(mesh, (mjtMeshBuiltin)n, params.data(), nparams)) { if (mjs_makeMesh(mesh, (mjtMeshBuiltin)n, params.data(), nparams)) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, "%s", mjs_getError(spec));
} }
} }
@@ -1590,7 +1594,9 @@ void mjXReader::OneSkin(XMLElement* elem, mjsSkin* skin, const mjVFS* vfs) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(skin->element, name.c_str()); if (mjs_setName(skin->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
auto file = ReadAttrFile(elem, "file", vfs, AssetDir()); auto file = ReadAttrFile(elem, "file", vfs, AssetDir());
if (file.has_value()) { if (file.has_value()) {
@@ -1680,7 +1686,9 @@ void mjXReader::OneMaterial(XMLElement* elem, mjsMaterial* material) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(material->element, name.c_str()); if (mjs_setName(material->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
bool tex_attributes_found = false; bool tex_attributes_found = false;
@@ -1729,7 +1737,9 @@ void mjXReader::OneJoint(XMLElement* elem, mjsJoint* joint) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(joint->element, name.c_str()); if (mjs_setName(joint->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (MapValue(elem, "type", &n, joint_map, joint_sz)) { if (MapValue(elem, "type", &n, joint_map, joint_sz)) {
joint->type = (mjtJoint)n; joint->type = (mjtJoint)n;
@@ -1777,7 +1787,9 @@ void mjXReader::OneGeom(XMLElement* elem, mjsGeom* geom) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(geom->element, name.c_str()); if (mjs_setName(geom->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) { if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) {
geom->type = (mjtGeom)n; geom->type = (mjtGeom)n;
@@ -1849,7 +1861,9 @@ void mjXReader::OneSite(XMLElement* elem, mjsSite* site) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(site->element, name.c_str()); if (mjs_setName(site->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) { if (MapValue(elem, "type", &n, geom_map, mjNGEOMTYPES)) {
site->type = (mjtGeom)n; site->type = (mjtGeom)n;
@@ -1882,7 +1896,9 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* camera) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(camera->element, name.c_str()); if (mjs_setName(camera->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "target", targetbody)) { if (ReadAttrTxt(elem, "target", targetbody)) {
mjs_setString(camera->targetbody, targetbody.c_str()); mjs_setString(camera->targetbody, targetbody.c_str());
@@ -1937,7 +1953,9 @@ void mjXReader::OneLight(XMLElement* elem, mjsLight* light) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(light->element, name.c_str()); if (mjs_setName(light->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "texture", texture)) { if (ReadAttrTxt(elem, "texture", texture)) {
mjs_setString(light->texture, texture.c_str()); mjs_setString(light->texture, texture.c_str());
@@ -1998,7 +2016,9 @@ void mjXReader::OnePair(XMLElement* elem, mjsPair* pair) {
// read other parameters // read other parameters
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(pair->element, name.c_str()); if (mjs_setName(pair->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttrInt(elem, "condim", &pair->condim); ReadAttrInt(elem, "condim", &pair->condim);
ReadAttr(elem, "solref", mjNREF, pair->solref, text, false, false); ReadAttr(elem, "solref", mjNREF, pair->solref, text, false, false);
@@ -2026,7 +2046,9 @@ void mjXReader::OneEquality(XMLElement* elem, mjsEquality* equality) {
// regular only // regular only
if (!readingdefaults) { if (!readingdefaults) {
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(equality->element, name.c_str()); if (mjs_setName(equality->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
switch (equality->type) { switch (equality->type) {
@@ -2162,7 +2184,9 @@ void mjXReader::OneTendon(XMLElement* elem, mjsTendon* tendon) {
// read attributes // read attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(tendon->element, name.c_str()); if (mjs_setName(tendon->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttrInt(elem, "group", &tendon->group); ReadAttrInt(elem, "group", &tendon->group);
if (ReadAttrTxt(elem, "material", material)) { if (ReadAttrTxt(elem, "material", material)) {
@@ -2205,7 +2229,9 @@ void mjXReader::OneActuator(XMLElement* elem, mjsActuator* actuator) {
// common attributes // common attributes
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(actuator->element, name.c_str()); if (mjs_setName(actuator->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttrInt(elem, "group", &actuator->group); ReadAttrInt(elem, "group", &actuator->group);
MapValue(elem, "ctrllimited", &actuator->ctrllimited, TFAuto_map, 3); MapValue(elem, "ctrllimited", &actuator->ctrllimited, TFAuto_map, 3);
@@ -2956,7 +2982,9 @@ void mjXReader::Custom(XMLElement* section) {
// read attributes // read attributes
ReadAttrTxt(elem, "name", elname, true); ReadAttrTxt(elem, "name", elname, true);
mjs_setName(numeric->element, elname.c_str()); if (mjs_setName(numeric->element, elname.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
if (ReadAttrInt(elem, "size", &numeric->size)) { if (ReadAttrInt(elem, "size", &numeric->size)) {
int sz = numeric->size < 500 ? numeric->size : 500; int sz = numeric->size < 500 ? numeric->size : 500;
for (int i=0; i < sz; i++) { for (int i=0; i < sz; i++) {
@@ -2987,7 +3015,9 @@ void mjXReader::Custom(XMLElement* section) {
// read attributes // read attributes
ReadAttrTxt(elem, "name", elname, true); ReadAttrTxt(elem, "name", elname, true);
mjs_setName(text->element, elname.c_str()); if (mjs_setName(text->element, elname.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
ReadAttrTxt(elem, "data", str, true); ReadAttrTxt(elem, "data", str, true);
if (str.empty()) { if (str.empty()) {
throw mjXError(elem, "text field cannot be empty"); throw mjXError(elem, "text field cannot be empty");
@@ -3007,7 +3037,9 @@ void mjXReader::Custom(XMLElement* section) {
// read attributes // read attributes
ReadAttrTxt(elem, "name", elname, true); ReadAttrTxt(elem, "name", elname, true);
mjs_setName(tuple->element, elname.c_str()); if (mjs_setName(tuple->element, elname.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
// read objects and add // read objects and add
XMLElement* obj = FirstChildElement(elem); XMLElement* obj = FirstChildElement(elem);
@@ -3223,7 +3255,9 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
texture->colorspace = (mjtColorSpace)n; texture->colorspace = (mjtColorSpace)n;
} }
if (ReadAttrTxt(elem, "name", texname)) { if (ReadAttrTxt(elem, "name", texname)) {
mjs_setName(texture->element, texname.c_str()); if (mjs_setName(texture->element, texname.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "content_type", content_type)) { if (ReadAttrTxt(elem, "content_type", content_type)) {
mjs_setString(texture->content_type, content_type.c_str()); mjs_setString(texture->content_type, content_type.c_str());
@@ -3315,7 +3349,9 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
// read attributes // read attributes
string name, content_type; string name, content_type;
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(hfield->element, name.c_str()); if (mjs_setName(hfield->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "content_type", content_type)) { if (ReadAttrTxt(elem, "content_type", content_type)) {
mjs_setString(hfield->content_type, content_type.c_str()); mjs_setString(hfield->content_type, content_type.c_str());
@@ -3482,7 +3518,9 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
// read attributes // read attributes
string name; string name;
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(joint->element, name.c_str()); if (mjs_setName(joint->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttrInt(elem, "group", &joint->group); ReadAttrInt(elem, "group", &joint->group);
MapValue(elem, "align", &joint->align, TFAuto_map, 3); MapValue(elem, "align", &joint->align, TFAuto_map, 3);
@@ -3554,7 +3592,9 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
// read attributes // read attributes
string name, childclass; string name, childclass;
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(pframe->element, name.c_str()); if (mjs_setName(pframe->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "childclass", childclass)) { if (ReadAttrTxt(elem, "childclass", childclass)) {
mjs_setString(pframe->childclass, childclass.c_str()); mjs_setString(pframe->childclass, childclass.c_str());
@@ -3626,13 +3666,13 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
// attach to parent // attach to parent
if (!mjs_attach(body->element, pframe->element, /*prefix=*/"", suffix.c_str())) { if (!mjs_attach(body->element, pframe->element, /*prefix=*/"", suffix.c_str())) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, "%s", mjs_getError(spec));
} }
} }
// delete subtree // delete subtree
if (mjs_delete(spec, subtree->element)) { if (mjs_delete(spec, subtree->element)) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, "%s", mjs_getError(spec));
} }
} }
@@ -3655,7 +3695,9 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
// read attributes // read attributes
string name, childclass; string name, childclass;
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(child->element, name.c_str()); if (mjs_setName(child->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
if (ReadAttrTxt(elem, "childclass", childclass)) { if (ReadAttrTxt(elem, "childclass", childclass)) {
mjs_setString(child->childclass, childclass.c_str()); mjs_setString(child->childclass, childclass.c_str());
@@ -3708,12 +3750,12 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
child = child_body->element; child = child_body->element;
} }
if (!mjs_attach(pframe->element, child, prefix.c_str(), "")) { if (!mjs_attach(pframe->element, child, prefix.c_str(), "")) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, "%s", mjs_getError(spec));
} }
} else { } else {
// only set frame to existing body // only set frame to existing body
if (mjs_setFrame(child_body->element, pframe)) { if (mjs_setFrame(child_body->element, pframe)) {
throw mjXError(elem, mjs_getError(spec)); throw mjXError(elem, "%s", mjs_getError(spec));
} }
} }
} }
@@ -3764,7 +3806,9 @@ void mjXReader::Contact(XMLElement* section) {
// read name and body names // read name and body names
if (ReadAttrTxt(elem, "name", exname)) { if (ReadAttrTxt(elem, "name", exname)) {
mjs_setName(exclude->element, exname.c_str()); if (mjs_setName(exclude->element, exname.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttrTxt(elem, "body1", exbody1, true); ReadAttrTxt(elem, "body1", exbody1, true);
mjs_setString(exclude->bodyname1, exbody1.c_str()); mjs_setString(exclude->bodyname1, exbody1.c_str());
@@ -3946,7 +3990,9 @@ void mjXReader::Sensor(XMLElement* section) {
// read name, noise, userdata // read name, noise, userdata
if (ReadAttrTxt(elem, "name", name)) { if (ReadAttrTxt(elem, "name", name)) {
mjs_setName(sensor->element, name.c_str()); if (mjs_setName(sensor->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
} }
ReadAttr(elem, "cutoff", 1, &sensor->cutoff, text); ReadAttr(elem, "cutoff", 1, &sensor->cutoff, text);
ReadAttr(elem, "noise", 1, &sensor->noise, text); ReadAttr(elem, "noise", 1, &sensor->noise, text);
@@ -4377,7 +4423,9 @@ void mjXReader::Keyframe(XMLElement* section) {
// read name, time // read name, time
ReadAttrTxt(elem, "name", name); ReadAttrTxt(elem, "name", name);
mjs_setName(key->element, name.c_str()); if (mjs_setName(key->element, name.c_str())) {
throw mjXError(elem, "%s", mjs_getError(spec));
}
ReadAttr(elem, "time", 1, &key->time, text); ReadAttr(elem, "time", 1, &key->time, text);
// read qpos // read qpos
+1
View File
@@ -42,6 +42,7 @@ macro(mujoco_test name)
) )
add_executable(${name} ${name}.cc) add_executable(${name} ${name}.cc)
target_link_options(${name} PRIVATE ${EXTRA_LINK_OPTIONS})
target_link_libraries(${name} mujoco fixture gmock) target_link_libraries(${name} mujoco fixture gmock)
if(_ARGS_MAIN_TARGET) if(_ARGS_MAIN_TARGET)
target_link_libraries(${name} ${_ARGS_MAIN_TARGET}) target_link_libraries(${name} ${_ARGS_MAIN_TARGET})
+1 -1
View File
@@ -1529,7 +1529,7 @@ TEST_F(XMLReaderTest, ParseReplicateRepeatedName) {
mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
EXPECT_THAT(spec, IsNull()) << error.data(); EXPECT_THAT(spec, IsNull()) << error.data();
EXPECT_THAT(error.data(), HasSubstr("repeated name 'b' in actuator")); EXPECT_THAT(error.data(), HasSubstr("repeated name 'b' in actuator"));
EXPECT_THAT(error.data(), HasSubstr("Element 'replicate'")); EXPECT_THAT(error.data(), HasSubstr("Element 'position'"));
} }
TEST_F(XMLReaderTest, RepeatedPrefix) { TEST_F(XMLReaderTest, RepeatedPrefix) {
+2
View File
@@ -1417,6 +1417,8 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
auto abs_path = p.path(); auto abs_path = p.path();
mjSpec* stemp = mj_parseXMLString(SaveAndReadXml(s).c_str(), 0, mjSpec* stemp = mj_parseXMLString(SaveAndReadXml(s).c_str(), 0,
error.data(), error.size()); error.data(), error.size());
ASSERT_THAT(stemp, NotNull())
<< "Failed to load " << xml.c_str() << ": " << error.data();
mjs_setString(stemp->modelfiledir, mjs_setString(stemp->modelfiledir,
abs_path.remove_filename().string().c_str()); abs_path.remove_filename().string().c_str());
mjModel* mtemp = mj_compile(stemp, nullptr); mjModel* mtemp = mj_compile(stemp, nullptr);