| Index: compiled/bindings.ipp | 
| =================================================================== | 
| --- a/compiled/bindings.ipp | 
| +++ b/compiled/bindings.ipp | 
| @@ -37,16 +37,17 @@ namespace bindings_internal | 
| { | 
| typedef void* TYPEID; | 
|  | 
| enum class TypeCategory | 
| { | 
| UNKNOWN, | 
| VOID, | 
| INT, | 
| +    FLOAT, | 
| DEPENDENT_STRING, | 
| OWNED_STRING, | 
| STRING_REF, | 
| CLASS_PTR | 
| }; | 
|  | 
| template<typename T> | 
| struct TypeInfo | 
| @@ -68,16 +69,19 @@ namespace bindings_internal | 
| constexpr operator TypeCategory() const | 
| { | 
| if (std::is_void<T>()) | 
| return TypeCategory::VOID; | 
|  | 
| if (std::is_integral<T>() || std::is_enum<T>()) | 
| return TypeCategory::INT; | 
|  | 
| +      if (std::is_floating_point<T>()) | 
| +        return TypeCategory::FLOAT; | 
| + | 
| if (std::is_same<DependentString, T>() || std::is_same<const DependentString, T>()) | 
| return TypeCategory::DEPENDENT_STRING; | 
|  | 
| if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>()) | 
| return TypeCategory::OWNED_STRING; | 
|  | 
| if (std::is_same<String&, T>() || std::is_same<const String&, T>() || | 
| std::is_same<DependentString&, T>()) | 
| @@ -127,49 +131,63 @@ namespace bindings_internal | 
| name[0] = '\0'; | 
|  | 
| // The function parameter is a pointer to the function pointer. | 
| // Emscripten's "function pointers" are actually integers indicating the | 
| // position in the call table. 0 represents nullptr. | 
| if (!*reinterpret_cast<int*>(function)) | 
| return; | 
|  | 
| +      char signature[256]; | 
| +      int pos = 0; | 
| +      if (returnType == TypeCategory::DEPENDENT_STRING || | 
| +          returnType == TypeCategory::OWNED_STRING) | 
| +      { | 
| +        // Objects aren't really returned but passed as parameter. Note that | 
| +        // this pointer might come before it but we don't care because both | 
| +        // are integers (pointers) as far as Emscripten is concerned. | 
| +        signature[pos++] = 'v'; | 
| +        signature[pos++] = 'i'; | 
| +      } | 
| +      else if (returnType == TypeCategory::VOID) | 
| +        signature[pos++] = 'v'; | 
| +      else if (returnType == TypeCategory::FLOAT) | 
| +        signature[pos++] = 'd'; | 
| +      else if (returnType == TypeCategory::INT || | 
| +          returnType == TypeCategory::STRING_REF || | 
| +          returnType == TypeCategory::CLASS_PTR) | 
| +      { | 
| +        signature[pos++] = 'i'; | 
| +      } | 
| +      else | 
| +        throw std::runtime_error("Unexpected function return type"); | 
| + | 
| +      if (instance_function) | 
| +      { | 
| +        // this pointer is an implicit parameter | 
| +        signature[pos++] = 'i'; | 
| +      } | 
| + | 
| for (const auto& item : argTypes) | 
| { | 
| -        if (item != TypeCategory::INT && item != TypeCategory::STRING_REF && | 
| -            item != TypeCategory::CLASS_PTR) | 
| +        if (item == TypeCategory::INT || item == TypeCategory::STRING_REF || | 
| +            item == TypeCategory::CLASS_PTR) | 
| { | 
| +          signature[pos++] = 'i'; | 
| +        } | 
| +        else if (item == TypeCategory::FLOAT) | 
| +          signature[pos++] = 'd'; | 
| +        else | 
| throw std::runtime_error("Unexpected function argument type"); | 
| -        } | 
| args.push_back(item); | 
| } | 
|  | 
| -      if (returnType != TypeCategory::VOID && returnType != TypeCategory::INT && | 
| -          returnType != TypeCategory::DEPENDENT_STRING && | 
| -          returnType != TypeCategory::OWNED_STRING && | 
| -          returnType != TypeCategory::STRING_REF && | 
| -          returnType != TypeCategory::CLASS_PTR) | 
| -      { | 
| -        throw std::runtime_error("Unexpected function return type"); | 
| -      } | 
| +      signature[pos] = 0; | 
|  | 
| -      effectiveArgs = args.size(); | 
| -      effectiveReturnType = returnType; | 
| -      if (instance_function) | 
| -        effectiveArgs++; | 
| - | 
| -      if (returnType == TypeCategory::DEPENDENT_STRING || | 
| -          returnType == TypeCategory::OWNED_STRING) | 
| -      { | 
| -        effectiveArgs++; | 
| -        effectiveReturnType = TypeCategory::VOID; | 
| -      } | 
| - | 
| -      get_function_name(function, effectiveArgs, | 
| -          effectiveReturnType == TypeCategory::VOID); | 
| +      get_function_name(function, signature); | 
| } | 
|  | 
| template<typename ReturnType, typename... Args> | 
| FunctionInfo(ReturnType (*function)(Args...)) | 
| : FunctionInfo(TypeInfo<ReturnType>(), | 
| TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, | 
| &function) | 
| { | 
| @@ -191,35 +209,32 @@ namespace bindings_internal | 
| { | 
| } | 
|  | 
| bool empty() const | 
| { | 
| return name[0] == '\0'; | 
| } | 
|  | 
| -    void get_function_name(void* ptr, int numArgs, bool voidResult) | 
| +    void get_function_name(void* ptr, char* signature) | 
| { | 
| // This is a hack, C++ won't let us get the mangled function name. | 
| // JavaScript is more dynamic so we pass the pointer to our function | 
| // there. With that and the function signature we can call the function - | 
| // with a full stack so that we will cause it to abort. Sometimes the | 
| // function we are calling will also be missing from the build. The result | 
| // is the same: abort() is called which in turn calls stackTrace(). By | 
| // replacing stackTrace() we get access to the call stack and search it | 
| // for the name of our function. | 
|  | 
| EM_ASM_ARGS({ | 
| -        var signature = $3 ? "v" : "i"; | 
| +        var signature = AsciiToString($2); | 
| var args = []; | 
| -        for (var i = 0; i < $2; i++) | 
| -        { | 
| -          signature += "i"; | 
| +        for (var i = 1; i < signature.length; i++) | 
| args.push(0); | 
| -        } | 
|  | 
| var oldPrint = Module.print; | 
| var oldPrintErr = Module.printErr; | 
| var oldStackTrace = stackTrace; | 
| var sp = Runtime.stackSave(); | 
| Module.print = function(){}; | 
| Module.printErr = function(){}; | 
| stackTrace = function() | 
| @@ -254,17 +269,17 @@ namespace bindings_internal | 
| } | 
| finally | 
| { | 
| Runtime.stackRestore(sp); | 
| Module.print = oldPrint; | 
| Module.printErr = oldPrintErr; | 
| stackTrace = oldStackTrace; | 
| } | 
| -      }, name, ptr, numArgs, voidResult); | 
| +      }, name, ptr, signature); | 
| } | 
| }; | 
|  | 
| class NoBaseClass | 
| { | 
| }; | 
|  | 
| struct PropertyInfo | 
| @@ -394,18 +409,21 @@ namespace bindings_internal | 
| if (i > 0) | 
| call_str += ", "; | 
| call_str += params[i]; | 
| } | 
| call_str += ")"; | 
|  | 
| if (call.returnType == TypeCategory::VOID) | 
| return "  " + call_str + ";\n"; | 
| -    else if (call.returnType == TypeCategory::INT) | 
| +    else if (call.returnType == TypeCategory::INT || | 
| +          call.returnType == TypeCategory::FLOAT) | 
| +    { | 
| return "  var result = " + call_str + ";\n"; | 
| +    } | 
| else if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| call.returnType == TypeCategory::OWNED_STRING) | 
| { | 
| std::string result; | 
| result += "  var string = createString();\n"; | 
| result += "  " + call_str + ";\n"; | 
| result += "  var result = readString(string);\n"; | 
| if (call.returnType == TypeCategory::OWNED_STRING) | 
| @@ -460,16 +478,18 @@ namespace bindings_internal | 
| prefix += ", "; | 
| prefix += buffer; | 
|  | 
| if (call.args[i] == TypeCategory::STRING_REF) | 
| { | 
| hasStringArgs = true; | 
| params.push_back(std::string("createString(") + buffer + ")"); | 
| } | 
| +      else if (call.args[i] == TypeCategory::CLASS_PTR) | 
| +        params.push_back(std::string(buffer) + "._pointer"); | 
| else | 
| params.push_back(buffer); | 
| } | 
| prefix += ")\n{\n"; | 
|  | 
| std::string suffix = "}"; | 
| if (call.returnType != TypeCategory::VOID) | 
| suffix = "  return result;\n" + suffix; | 
|  |