Repository: brendanzab/gl-rs (language: rust)

Issue:
Binary sizes become huge due to many, many strings
Should add a (feature?) or use ndebug to not enable these.



Discussion / hints:
What if we replace `panic!("The function glSomething is not loaded")` by `panic!("The function {} is not loaded", "glSomething")`?

We already have the list of function names in the binary and they can't be removed (since they need to be passed to `GetProcAddr`). So if we extract the name from the error message, rust can use the same memory location to store strings used at multiple places.

I tried doing the change described in my previous comment, turns out it doesn't change anything in the binary size. I should have looked at the strings before doing this blindly.

Ack. This is pretty annoying. It is nice to have better error messages, but the binary size problem makes it a little untenable... I dunno. Perhaps we should just panic without a message?

@tomaka is Rust actually merging those constants? (I don't know if you checked)

@bjz I find that acceptable, backtraces are fine IMO.

I will tackle this.


Fix patch:
diff --git a/gl_generator/generators/global_gen.rs b/gl_generator/generators/global_gen.rs
index aa3067b9..64669d24 100644
--- a/gl_generator/generators/global_gen.rs
+++ b/gl_generator/generators/global_gen.rs
@@ -29,7 +29,7 @@ impl super::Generator for GlobalGenerator {
         try!(write_fnptr_struct_def(dest));
         try!(write_ptrs(registry, dest));
         try!(write_fn_mods(registry, &ns, dest));
-        try!(write_failing_fns(registry, dest));
+        try!(write_panicking_fns(registry, &ns, dest));
         try!(write_load_fn(registry, dest));
         Ok(())
     }
@@ -139,9 +139,9 @@ fn write_fnptr_struct_def<W>(dest: &mut W) -> IoResult<()> where W: Writer {
     writeln!(dest, r#"
         impl FnPtr {{
             /// Creates a `FnPtr` from a load attempt.
-            pub fn new(ptr: *const __gl_imports::libc::c_void, failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {{
+            pub fn new(ptr: *const __gl_imports::libc::c_void) -> FnPtr {{
                 if ptr.is_null() {{
-                    FnPtr {{ f: failing_fn, is_loaded: false }}
+                    FnPtr {{ f: missing_fn_panic as *const __gl_imports::libc::c_void, is_loaded: false }}
                 }} else {{
                     FnPtr {{ f: ptr, is_loaded: true }}
                 }}
@@ -155,7 +155,7 @@ fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> IoResult<()> where W: Wri
     let storages = registry.cmd_iter().map(|c| {
         format!(
             "pub static mut {name}: FnPtr = FnPtr {{
-                f: failing::{name} as *const libc::c_void,
+                f: super::missing_fn_panic as *const libc::c_void,
                 is_loaded: false
             }};",
             name = c.proto.ident
@@ -166,7 +166,6 @@ fn write_ptrs<W>(registry: &Registry, dest: &mut W) -> IoResult<()> where W: Wri
         mod storage {{
             #![allow(non_snake_case)]
             use super::__gl_imports::libc;
-            use super::failing;
             use super::FnPtr;
 
             {storages}
@@ -194,7 +193,7 @@ fn write_fn_mods<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()>
             #[unstable]
             #[allow(non_snake_case)]
             pub mod {fnname} {{
-                use super::{{failing, storage, metaloadfn}};
+                use super::{{storage, metaloadfn}};
                 use super::FnPtr;
 
                 #[inline]
@@ -206,8 +205,7 @@ fn write_fn_mods<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()>
                 #[allow(dead_code)]
                 pub fn load_with<F>(loadfn: F) where F: FnMut(&str) -> *const super::__gl_imports::libc::c_void {{
                     unsafe {{
-                        storage::{fnname} = FnPtr::new(metaloadfn(loadfn, "{symbol}", {fallbacks}),
-                            failing::{fnname} as *const super::__gl_imports::libc::c_void)
+                        storage::{fnname} = FnPtr::new(metaloadfn(loadfn, "{symbol}", {fallbacks}))
                     }}
                 }}
             }}
@@ -217,30 +215,17 @@ fn write_fn_mods<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()>
     Ok(())
 }
 
-/// Creates a `failing` module which contains one function per GL command.
+/// Creates a `missing_fn_panic` function.
 ///
-/// These functions are the mocks that are called if the real function could not be loaded.
-fn write_failing_fns<W>(registry: &Registry, dest: &mut W) -> IoResult<()> where W: Writer {
-    let functions = registry.cmd_iter().map(|c| {
-        (format!(
-            "#[allow(non_snake_case)] #[allow(unused_variables)] #[allow(dead_code)]
-            pub extern \"system\" fn {name}({params}) -> {return_suffix} {{ \
-                panic!(\"`{name}` was not loaded\") \
-            }}",
-            name = c.proto.ident,
-            params = super::gen_parameters(c, true, true).connect(", "),
-            return_suffix = super::gen_return_type(c)
-        ))
-    }).collect::<Vec<String>>().connect("\n");
-
-    writeln!(dest, r#"
-        mod failing {{
-            use super::types;
-            use super::__gl_imports;
-
-            {functions}
+/// This function is the mock that is called if the real function could not be called.
+fn write_panicking_fns<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()> where W: Writer {
+    writeln!(dest,
+        "
+        #[inline(never)]
+        fn missing_fn_panic() -> ! {{
+            panic!(\"{ns} function was not loaded\")
         }}
-    "#, functions = functions)
+        ", ns = ns)
 }
 
 /// Creates the `load_with` function.
diff --git a/gl_generator/generators/struct_gen.rs b/gl_generator/generators/struct_gen.rs
index 773eba3e..5a1377f5 100644
--- a/gl_generator/generators/struct_gen.rs
+++ b/gl_generator/generators/struct_gen.rs
@@ -25,7 +25,7 @@ impl super::Generator for StructGenerator {
         try!(write_type_aliases(&ns, dest));
         try!(write_enums(registry, dest));
         try!(write_fnptr_struct_def(dest));
-        try!(write_failing_fns(registry, dest));
+        try!(write_panicking_fns(registry, &ns, dest));
         try!(write_struct(registry, &ns, dest));
         try!(write_impl(registry, &ns, dest));
         Ok(())
@@ -90,10 +90,12 @@ fn write_fnptr_struct_def<W>(dest: &mut W) -> IoResult<()> where W: Writer {
     writeln!(dest, r#"
         impl FnPtr {{
             /// Creates a `FnPtr` from a load attempt.
-            fn new(ptr: *const __gl_imports::libc::c_void,
-                failing_fn: *const __gl_imports::libc::c_void) -> FnPtr {{
+            fn new(ptr: *const __gl_imports::libc::c_void) -> FnPtr {{
                 if ptr.is_null() {{
-                    FnPtr {{ f: failing_fn, is_loaded: false }}
+                    FnPtr {{
+                        f: missing_fn_panic as *const __gl_imports::libc::c_void,
+                        is_loaded: false
+                    }}
                 }} else {{
                     FnPtr {{ f: ptr, is_loaded: true }}
                 }}
@@ -111,33 +113,17 @@ fn write_fnptr_struct_def<W>(dest: &mut W) -> IoResult<()> where W: Writer {
     "#)
 }
 
-/// Creates a `failing` module which contains one function per GL command.
+/// Creates a `panicking` module which contains one function per GL command.
 ///
 /// These functions are the mocks that are called if the real function could not be loaded.
-fn write_failing_fns<W>(registry: &Registry, dest: &mut W) -> IoResult<()> where W: Writer {
-    let fns = registry.cmd_iter().map(|c| {
-        format!(r#"
-            #[allow(unused_variables)]
-            #[allow(non_snake_case)]
-            #[allow(dead_code)]
-            pub extern "system" fn {name}({params}) -> {return_suffix} {{
-                panic!("`{name}` was not loaded")
-            }}
-            "#,
-            name = c.proto.ident,
-            params = super::gen_parameters(c, true, true).connect(", "),
-            return_suffix = super::gen_return_type(c)
-        )
-    }).collect::<Vec<String>>().connect("\n");
-
-    writeln!(dest, r#"
-        mod failing {{
-            use super::types;
-            use super::__gl_imports;
-
-            {fns}
+fn write_panicking_fns<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()> where W: Writer {
+    writeln!(dest,
+        "
+        #[inline(never)]
+        fn missing_fn_panic() -> ! {{
+            panic!(\"{ns} function was not loaded\")
         }}
-    "#, fns = fns)
+        ", ns = ns)
 }
 
 /// Creates a structure which stores all the `FnPtr` of the bindings.
@@ -219,7 +205,7 @@ fn write_impl<W>(registry: &Registry, ns: &Ns, dest: &mut W) -> IoResult<()> whe
         loadings = registry.cmd_iter().map(|c| {
             let fallbacks = registry.aliases.get(&c.proto.ident);
             format!(
-                "{name}: FnPtr::new(metaloadfn(\"{symbol}\", {fb}), failing::{name} as *const __gl_imports::libc::c_void),",
+                "{name}: FnPtr::new(metaloadfn(\"{symbol}\", {fb})),",
                 name = c.proto.ident,
                 symbol = super::gen_symbol_name(ns, c.proto.ident.as_slice()),
                 fb = match fallbacks {


Test patch: