Fully generate Emscripten bindings for MuJoCo functions.

PiperOrigin-RevId: 830975595
Change-Id: Ie4d0d16c80f490abaa5ae8218628ac1b3d32c0ef
This commit is contained in:
Matias Manevi
2025-11-11 10:31:00 -08:00
committed by Copybara-Service
parent ff037f2e78
commit 54661ab140
5 changed files with 96 additions and 178 deletions
+80 -81
View File
@@ -12485,6 +12485,8 @@ void mjd_subQuat_wrapper(const NumberArray& qa, const NumberArray& qb, const val
}
EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("parseXMLString", &parseXMLString, take_ownership());
function("error", &error_wrapper);
function("mj_resetCallbacks", &mj_resetCallbacks);
function("mj_version", &mj_version);
function("mju_bandDiag", &mju_bandDiag);
@@ -12498,6 +12500,7 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mju_Halton", &mju_Halton);
function("mju_sigmoid", &mju_sigmoid);
function("mj_copyBack", &mj_copyBack_wrapper);
function("mj_saveLastXML", &mj_saveLastXML_wrapper);
function("mj_step", &mj_step_wrapper);
function("mj_step1", &mj_step1_wrapper);
function("mj_step2", &mj_step2_wrapper);
@@ -12514,6 +12517,7 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mj_resetDataDebug", &mj_resetDataDebug_wrapper);
function("mj_resetDataKeyframe", &mj_resetDataKeyframe_wrapper);
function("mj_setConst", &mj_setConst_wrapper);
function("mj_setLengthRange", &mj_setLengthRange_wrapper);
function("mjs_activatePlugin", &mjs_activatePlugin_wrapper);
function("mjs_setDeepCopy", &mjs_setDeepCopy_wrapper);
function("mj_printFormattedModel", &mj_printFormattedModel_wrapper);
@@ -12521,6 +12525,7 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mj_printFormattedData", &mj_printFormattedData_wrapper);
function("mj_printData", &mj_printData_wrapper);
function("mju_printMat", &mju_printMat_wrapper);
function("mju_printMatSparse", &mju_printMatSparse_wrapper);
function("mj_printScene", &mj_printScene_wrapper);
function("mj_printFormattedScene", &mj_printFormattedScene_wrapper);
function("mj_fwdPosition", &mj_fwdPosition_wrapper);
@@ -12552,31 +12557,58 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mj_crb", &mj_crb_wrapper);
function("mj_makeM", &mj_makeM_wrapper);
function("mj_factorM", &mj_factorM_wrapper);
function("mj_solveM", &mj_solveM_wrapper);
function("mj_solveM2", &mj_solveM2_wrapper);
function("mj_comVel", &mj_comVel_wrapper);
function("mj_passive", &mj_passive_wrapper);
function("mj_subtreeVel", &mj_subtreeVel_wrapper);
function("mj_rne", &mj_rne_wrapper);
function("mj_rnePostConstraint", &mj_rnePostConstraint_wrapper);
function("mj_collision", &mj_collision_wrapper);
function("mj_makeConstraint", &mj_makeConstraint_wrapper);
function("mj_island", &mj_island_wrapper);
function("mj_projectConstraint", &mj_projectConstraint_wrapper);
function("mj_referenceConstraint", &mj_referenceConstraint_wrapper);
function("mj_constraintUpdate", &mj_constraintUpdate_wrapper);
function("mj_stateSize", &mj_stateSize_wrapper);
function("mj_getState", &mj_getState_wrapper);
function("mj_extractState", &mj_extractState_wrapper);
function("mj_setState", &mj_setState_wrapper);
function("mj_setKeyframe", &mj_setKeyframe_wrapper);
function("mj_addContact", &mj_addContact_wrapper);
function("mj_isPyramidal", &mj_isPyramidal_wrapper);
function("mj_isSparse", &mj_isSparse_wrapper);
function("mj_isDual", &mj_isDual_wrapper);
function("mj_mulJacVec", &mj_mulJacVec_wrapper);
function("mj_mulJacTVec", &mj_mulJacTVec_wrapper);
function("mj_jac", &mj_jac_wrapper);
function("mj_jacBody", &mj_jacBody_wrapper);
function("mj_jacBodyCom", &mj_jacBodyCom_wrapper);
function("mj_jacSubtreeCom", &mj_jacSubtreeCom_wrapper);
function("mj_jacGeom", &mj_jacGeom_wrapper);
function("mj_jacSite", &mj_jacSite_wrapper);
function("mj_jacPointAxis", &mj_jacPointAxis_wrapper);
function("mj_jacDot", &mj_jacDot_wrapper);
function("mj_angmomMat", &mj_angmomMat_wrapper);
function("mj_name2id", &mj_name2id_wrapper);
function("mj_id2name", &mj_id2name_wrapper);
function("mj_fullM", &mj_fullM_wrapper);
function("mj_mulM", &mj_mulM_wrapper);
function("mj_mulM2", &mj_mulM2_wrapper);
function("mj_addM", &mj_addM_wrapper);
function("mj_applyFT", &mj_applyFT_wrapper);
function("mj_objectVelocity", &mj_objectVelocity_wrapper);
function("mj_objectAcceleration", &mj_objectAcceleration_wrapper);
function("mj_geomDistance", &mj_geomDistance_wrapper);
function("mj_contactForce", &mj_contactForce_wrapper);
function("mj_differentiatePos", &mj_differentiatePos_wrapper);
function("mj_integratePos", &mj_integratePos_wrapper);
function("mj_normalizeQuat", &mj_normalizeQuat_wrapper);
function("mj_local2Global", &mj_local2Global_wrapper);
function("mj_getTotalmass", &mj_getTotalmass_wrapper);
function("mj_setTotalmass", &mj_setTotalmass_wrapper);
function("mj_versionString", &mj_versionString_wrapper);
function("mj_multiRay", &mj_multiRay_wrapper);
function("mj_ray", &mj_ray_wrapper);
function("mj_rayHfield", &mj_rayHfield_wrapper);
function("mj_rayMesh", &mj_rayMesh_wrapper);
@@ -12631,7 +12663,34 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mju_unit4", &mju_unit4_wrapper);
function("mju_copy4", &mju_copy4_wrapper);
function("mju_normalize4", &mju_normalize4_wrapper);
function("mju_zero", &mju_zero_wrapper);
function("mju_fill", &mju_fill_wrapper);
function("mju_copy", &mju_copy_wrapper);
function("mju_sum", &mju_sum_wrapper);
function("mju_L1", &mju_L1_wrapper);
function("mju_scl", &mju_scl_wrapper);
function("mju_add", &mju_add_wrapper);
function("mju_sub", &mju_sub_wrapper);
function("mju_addTo", &mju_addTo_wrapper);
function("mju_subFrom", &mju_subFrom_wrapper);
function("mju_addToScl", &mju_addToScl_wrapper);
function("mju_addScl", &mju_addScl_wrapper);
function("mju_normalize", &mju_normalize_wrapper);
function("mju_norm", &mju_norm_wrapper);
function("mju_dot", &mju_dot_wrapper);
function("mju_mulMatVec", &mju_mulMatVec_wrapper);
function("mju_mulMatTVec", &mju_mulMatTVec_wrapper);
function("mju_mulVecMatVec", &mju_mulVecMatVec_wrapper);
function("mju_transpose", &mju_transpose_wrapper);
function("mju_symmetrize", &mju_symmetrize_wrapper);
function("mju_eye", &mju_eye_wrapper);
function("mju_mulMatMat", &mju_mulMatMat_wrapper);
function("mju_mulMatMatT", &mju_mulMatMatT_wrapper);
function("mju_mulMatTMat", &mju_mulMatTMat_wrapper);
function("mju_sqrMatTD", &mju_sqrMatTD_wrapper);
function("mju_transformSpatial", &mju_transformSpatial_wrapper);
function("mju_dense2sparse", &mju_dense2sparse_wrapper);
function("mju_sparse2dense", &mju_sparse2dense_wrapper);
function("mju_rotVecQuat", &mju_rotVecQuat_wrapper);
function("mju_negQuat", &mju_negQuat_wrapper);
function("mju_mulQuat", &mju_mulQuat_wrapper);
@@ -12649,15 +12708,36 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mju_mulPose", &mju_mulPose_wrapper);
function("mju_negPose", &mju_negPose_wrapper);
function("mju_trnVecPose", &mju_trnVecPose_wrapper);
function("mju_cholFactor", &mju_cholFactor_wrapper);
function("mju_cholSolve", &mju_cholSolve_wrapper);
function("mju_cholUpdate", &mju_cholUpdate_wrapper);
function("mju_cholFactorBand", &mju_cholFactorBand_wrapper);
function("mju_cholSolveBand", &mju_cholSolveBand_wrapper);
function("mju_band2Dense", &mju_band2Dense_wrapper);
function("mju_dense2Band", &mju_dense2Band_wrapper);
function("mju_bandMulMatVec", &mju_bandMulMatVec_wrapper);
function("mju_eig3", &mju_eig3_wrapper);
function("mju_boxQP", &mju_boxQP_wrapper);
function("mju_muscleGain", &mju_muscleGain_wrapper);
function("mju_muscleBias", &mju_muscleBias_wrapper);
function("mju_muscleDynamics", &mju_muscleDynamics_wrapper);
function("mju_encodePyramid", &mju_encodePyramid_wrapper);
function("mju_decodePyramid", &mju_decodePyramid_wrapper);
function("mju_type2Str", &mju_type2Str_wrapper);
function("mju_str2Type", &mju_str2Type_wrapper);
function("mju_writeNumBytes", &mju_writeNumBytes_wrapper);
function("mju_warningText", &mju_warningText_wrapper);
function("mju_isZero", &mju_isZero_wrapper);
function("mju_standardNormal", &mju_standardNormal_wrapper);
function("mju_f2n", &mju_f2n_wrapper);
function("mju_n2f", &mju_n2f_wrapper);
function("mju_d2n", &mju_d2n_wrapper);
function("mju_n2d", &mju_n2d_wrapper);
function("mju_insertionSort", &mju_insertionSort_wrapper);
function("mju_insertionSortInt", &mju_insertionSortInt_wrapper);
function("mjd_transitionFD", &mjd_transitionFD_wrapper);
function("mjd_inverseFD", &mjd_inverseFD_wrapper);
function("mjd_subQuat", &mjd_subQuat_wrapper);
function("mjd_quatIntegrate", &mjd_quatIntegrate_wrapper);
function("mjs_attach", &mjs_attach_wrapper);
function("mjs_addBody", &mjs_addBody_wrapper);
@@ -12778,87 +12858,6 @@ EMSCRIPTEN_BINDINGS(mujoco_functions) {
function("mjs_asTexture", &mjs_asTexture_wrapper);
function("mjs_asMaterial", &mjs_asMaterial_wrapper);
function("mjs_asPlugin", &mjs_asPlugin_wrapper);
function("parseXMLString", &parseXMLString, take_ownership());
function("error", &error_wrapper);
function("mju_printMatSparse", &mju_printMatSparse_wrapper);
function("mj_solveM", &mj_solveM_wrapper);
function("mj_solveM2", &mj_solveM2_wrapper);
function("mj_rne", &mj_rne_wrapper);
function("mj_saveLastXML", &mj_saveLastXML_wrapper);
function("mj_setLengthRange", &mj_setLengthRange_wrapper);
function("mj_constraintUpdate", &mj_constraintUpdate_wrapper);
function("mj_getState", &mj_getState_wrapper);
function("mj_setState", &mj_setState_wrapper);
function("mj_mulJacVec", &mj_mulJacVec_wrapper);
function("mj_mulJacTVec", &mj_mulJacTVec_wrapper);
function("mj_jac", &mj_jac_wrapper);
function("mj_jacBody", &mj_jacBody_wrapper);
function("mj_jacBodyCom", &mj_jacBodyCom_wrapper);
function("mj_jacSubtreeCom", &mj_jacSubtreeCom_wrapper);
function("mj_jacGeom", &mj_jacGeom_wrapper);
function("mj_jacSite", &mj_jacSite_wrapper);
function("mj_jacPointAxis", &mj_jacPointAxis_wrapper);
function("mj_jacDot", &mj_jacDot_wrapper);
function("mj_angmomMat", &mj_angmomMat_wrapper);
function("mj_fullM", &mj_fullM_wrapper);
function("mj_mulM", &mj_mulM_wrapper);
function("mj_mulM2", &mj_mulM2_wrapper);
function("mj_addM", &mj_addM_wrapper);
function("mj_applyFT", &mj_applyFT_wrapper);
function("mj_geomDistance", &mj_geomDistance_wrapper);
function("mj_differentiatePos", &mj_differentiatePos_wrapper);
function("mj_integratePos", &mj_integratePos_wrapper);
function("mj_normalizeQuat", &mj_normalizeQuat_wrapper);
function("mj_multiRay", &mj_multiRay_wrapper);
function("mju_zero", &mju_zero_wrapper);
function("mju_fill", &mju_fill_wrapper);
function("mju_copy", &mju_copy_wrapper);
function("mju_sum", &mju_sum_wrapper);
function("mju_L1", &mju_L1_wrapper);
function("mju_scl", &mju_scl_wrapper);
function("mju_add", &mju_add_wrapper);
function("mju_sub", &mju_sub_wrapper);
function("mju_addTo", &mju_addTo_wrapper);
function("mju_subFrom", &mju_subFrom_wrapper);
function("mju_addToScl", &mju_addToScl_wrapper);
function("mju_addScl", &mju_addScl_wrapper);
function("mju_normalize", &mju_normalize_wrapper);
function("mju_norm", &mju_norm_wrapper);
function("mju_dot", &mju_dot_wrapper);
function("mju_mulMatVec", &mju_mulMatVec_wrapper);
function("mju_mulMatTVec", &mju_mulMatTVec_wrapper);
function("mju_mulVecMatVec", &mju_mulVecMatVec_wrapper);
function("mju_transpose", &mju_transpose_wrapper);
function("mju_symmetrize", &mju_symmetrize_wrapper);
function("mju_eye", &mju_eye_wrapper);
function("mju_mulMatMat", &mju_mulMatMat_wrapper);
function("mju_mulMatMatT", &mju_mulMatMatT_wrapper);
function("mju_mulMatTMat", &mju_mulMatTMat_wrapper);
function("mju_sqrMatTD", &mju_sqrMatTD_wrapper);
function("mju_dense2sparse", &mju_dense2sparse_wrapper);
function("mju_sparse2dense", &mju_sparse2dense_wrapper);
function("mju_cholFactor", &mju_cholFactor_wrapper);
function("mju_cholSolve", &mju_cholSolve_wrapper);
function("mju_cholUpdate", &mju_cholUpdate_wrapper);
function("mju_cholFactorBand", &mju_cholFactorBand_wrapper);
function("mju_cholSolveBand", &mju_cholSolveBand_wrapper);
function("mju_band2Dense", &mju_band2Dense_wrapper);
function("mju_dense2Band", &mju_dense2Band_wrapper);
function("mju_bandMulMatVec", &mju_bandMulMatVec_wrapper);
function("mju_boxQP", &mju_boxQP_wrapper);
function("mju_encodePyramid", &mju_encodePyramid_wrapper);
function("mju_decodePyramid", &mju_decodePyramid_wrapper);
function("mju_isZero", &mju_isZero_wrapper);
function("mju_f2n", &mju_f2n_wrapper);
function("mju_n2f", &mju_n2f_wrapper);
function("mju_d2n", &mju_d2n_wrapper);
function("mju_n2d", &mju_n2d_wrapper);
function("mju_insertionSort", &mju_insertionSort_wrapper);
function("mju_insertionSortInt", &mju_insertionSortInt_wrapper);
function("mjd_transitionFD", &mjd_transitionFD_wrapper);
function("mjd_inverseFD", &mjd_inverseFD_wrapper);
function("mjd_subQuat", &mjd_subQuat_wrapper);
class_<WasmBuffer<float>>("FloatBuffer")
.constructor<int>()
.class_function("FromArray", &WasmBuffer<float>::FromArray)
+1 -3
View File
@@ -17,7 +17,6 @@ from introspect import enums as introspect_enums
from introspect import functions as introspect_functions
from wasm.codegen.generators import common
from wasm.codegen.generators import constants
from wasm.codegen.generators import enums
from wasm.codegen.generators import functions
from wasm.codegen.generators import structs
@@ -53,8 +52,7 @@ class BindingBuilder:
functions_to_bind: dict[str, ast_nodes.FunctionDecl] = {}
for name, func in introspect_functions.FUNCTIONS.items():
if not functions.is_excluded_function_name(name):
if name not in constants.BOUNDCHECK_FUNCS:
functions_to_bind[name] = func
functions_to_bind[name] = func
generator = functions.Generator(functions_to_bind)
self.markers_and_content += generator.generate()
+11 -12
View File
@@ -366,18 +366,18 @@ class Generator:
code = []
for func in self.wrapper_bind_functions:
wrapper_code = generate_function_wrapper(func)
code.append(wrapper_code)
if func.name not in constants.BOUNDCHECK_FUNCS:
wrapper_code = generate_function_wrapper(func)
code.append(wrapper_code)
return "\n\n".join(code)
def _generate_direct_bindable_functions(self) -> str:
def _generate_direct_bindable_functions(self) -> list[str]:
"""Generates Embind bindings for all directly bindable functions."""
result = ""
result = []
for func in self.direct_bind_functions:
result += code_builder.INDENT
result += self._generate_function_binding(func)
result.append(self._generate_function_binding(func))
return result
@@ -390,15 +390,14 @@ class Generator:
if is_wrapper:
cpp_func += "_wrapper"
return f'function("{js_name}", &{cpp_func});\n'
return f'function("{js_name}", &{cpp_func});'
def _generate_wrapper_bindable_functions(self) -> str:
def _generate_wrapper_bindable_functions(self) -> list[str]:
"""Generates Embind bindings for all functions that need wrappers."""
result = ""
result = []
for func in self.wrapper_bind_functions:
result += code_builder.INDENT
result += self._generate_function_binding(func, True)
result.append(self._generate_function_binding(func, True))
return result
@@ -411,5 +410,5 @@ class Generator:
return [
("// {{ WRAPPER_FUNCTIONS }}", [wrapper_functions]),
("// {{ FUNCTION_BINDINGS }}", [function_bindings]),
("// {{ FUNCTION_BINDINGS }}", ["\n".join(function_bindings)]),
]
+1 -79
View File
@@ -1300,87 +1300,9 @@ void mjd_subQuat_wrapper(const NumberArray& qa, const NumberArray& qb, const val
}
EMSCRIPTEN_BINDINGS(mujoco_functions) {
// {{ FUNCTION_BINDINGS }}
function("parseXMLString", &parseXMLString, take_ownership());
function("error", &error_wrapper);
function("mju_printMatSparse", &mju_printMatSparse_wrapper);
function("mj_solveM", &mj_solveM_wrapper);
function("mj_solveM2", &mj_solveM2_wrapper);
function("mj_rne", &mj_rne_wrapper);
function("mj_saveLastXML", &mj_saveLastXML_wrapper);
function("mj_setLengthRange", &mj_setLengthRange_wrapper);
function("mj_constraintUpdate", &mj_constraintUpdate_wrapper);
function("mj_getState", &mj_getState_wrapper);
function("mj_setState", &mj_setState_wrapper);
function("mj_mulJacVec", &mj_mulJacVec_wrapper);
function("mj_mulJacTVec", &mj_mulJacTVec_wrapper);
function("mj_jac", &mj_jac_wrapper);
function("mj_jacBody", &mj_jacBody_wrapper);
function("mj_jacBodyCom", &mj_jacBodyCom_wrapper);
function("mj_jacSubtreeCom", &mj_jacSubtreeCom_wrapper);
function("mj_jacGeom", &mj_jacGeom_wrapper);
function("mj_jacSite", &mj_jacSite_wrapper);
function("mj_jacPointAxis", &mj_jacPointAxis_wrapper);
function("mj_jacDot", &mj_jacDot_wrapper);
function("mj_angmomMat", &mj_angmomMat_wrapper);
function("mj_fullM", &mj_fullM_wrapper);
function("mj_mulM", &mj_mulM_wrapper);
function("mj_mulM2", &mj_mulM2_wrapper);
function("mj_addM", &mj_addM_wrapper);
function("mj_applyFT", &mj_applyFT_wrapper);
function("mj_geomDistance", &mj_geomDistance_wrapper);
function("mj_differentiatePos", &mj_differentiatePos_wrapper);
function("mj_integratePos", &mj_integratePos_wrapper);
function("mj_normalizeQuat", &mj_normalizeQuat_wrapper);
function("mj_multiRay", &mj_multiRay_wrapper);
function("mju_zero", &mju_zero_wrapper);
function("mju_fill", &mju_fill_wrapper);
function("mju_copy", &mju_copy_wrapper);
function("mju_sum", &mju_sum_wrapper);
function("mju_L1", &mju_L1_wrapper);
function("mju_scl", &mju_scl_wrapper);
function("mju_add", &mju_add_wrapper);
function("mju_sub", &mju_sub_wrapper);
function("mju_addTo", &mju_addTo_wrapper);
function("mju_subFrom", &mju_subFrom_wrapper);
function("mju_addToScl", &mju_addToScl_wrapper);
function("mju_addScl", &mju_addScl_wrapper);
function("mju_normalize", &mju_normalize_wrapper);
function("mju_norm", &mju_norm_wrapper);
function("mju_dot", &mju_dot_wrapper);
function("mju_mulMatVec", &mju_mulMatVec_wrapper);
function("mju_mulMatTVec", &mju_mulMatTVec_wrapper);
function("mju_mulVecMatVec", &mju_mulVecMatVec_wrapper);
function("mju_transpose", &mju_transpose_wrapper);
function("mju_symmetrize", &mju_symmetrize_wrapper);
function("mju_eye", &mju_eye_wrapper);
function("mju_mulMatMat", &mju_mulMatMat_wrapper);
function("mju_mulMatMatT", &mju_mulMatMatT_wrapper);
function("mju_mulMatTMat", &mju_mulMatTMat_wrapper);
function("mju_sqrMatTD", &mju_sqrMatTD_wrapper);
function("mju_dense2sparse", &mju_dense2sparse_wrapper);
function("mju_sparse2dense", &mju_sparse2dense_wrapper);
function("mju_cholFactor", &mju_cholFactor_wrapper);
function("mju_cholSolve", &mju_cholSolve_wrapper);
function("mju_cholUpdate", &mju_cholUpdate_wrapper);
function("mju_cholFactorBand", &mju_cholFactorBand_wrapper);
function("mju_cholSolveBand", &mju_cholSolveBand_wrapper);
function("mju_band2Dense", &mju_band2Dense_wrapper);
function("mju_dense2Band", &mju_dense2Band_wrapper);
function("mju_bandMulMatVec", &mju_bandMulMatVec_wrapper);
function("mju_boxQP", &mju_boxQP_wrapper);
function("mju_encodePyramid", &mju_encodePyramid_wrapper);
function("mju_decodePyramid", &mju_decodePyramid_wrapper);
function("mju_isZero", &mju_isZero_wrapper);
function("mju_f2n", &mju_f2n_wrapper);
function("mju_n2f", &mju_n2f_wrapper);
function("mju_d2n", &mju_d2n_wrapper);
function("mju_n2d", &mju_n2d_wrapper);
function("mju_insertionSort", &mju_insertionSort_wrapper);
function("mju_insertionSortInt", &mju_insertionSortInt_wrapper);
function("mjd_transitionFD", &mjd_transitionFD_wrapper);
function("mjd_inverseFD", &mjd_inverseFD_wrapper);
function("mjd_subQuat", &mjd_subQuat_wrapper);
// {{ FUNCTION_BINDINGS }}
class_<WasmBuffer<float>>("FloatBuffer")
.constructor<int>()
.class_function("FromArray", &WasmBuffer<float>::FromArray)
+3 -3
View File
@@ -933,7 +933,7 @@ class FunctionsGeneratorTest(absltest.TestCase):
)
self.assertEqual(
self.generator._generate_function_binding(func_simple_void),
'function("do_nothing", &do_nothing);\n',
'function("do_nothing", &do_nothing);',
)
def test_generate_direct_bindable_functions_simple_filter(self):
@@ -957,8 +957,8 @@ class FunctionsGeneratorTest(absltest.TestCase):
})
generated_code = self.generator._generate_direct_bindable_functions()
self.assertIn('function("direct_bind", &direct_bind);\n', generated_code)
self.assertNotIn('function("needs_wrap", &needs_wrap);\n', generated_code)
self.assertIn('function("direct_bind", &direct_bind);', generated_code)
self.assertNotIn('function("needs_wrap", &needs_wrap);', generated_code)
if __name__ == "__main__":