| Index: compiled/bindings.h | 
| =================================================================== | 
| new file mode 100644 | 
| --- /dev/null | 
| +++ b/compiled/bindings.h | 
| @@ -0,0 +1,707 @@ | 
| +#pragma once | 
| + | 
| +#include <cstdio> | 
| +#include <cstdlib> | 
| +#include <cstring> | 
| +#include <exception> | 
| +#include <map> | 
| +#include <string> | 
| +#include <type_traits> | 
| +#include <utility> | 
| +#include <vector> | 
| + | 
| +#include <emscripten.h> | 
| + | 
| +#include "String.h" | 
| +#include "intrusive_ptr.h" | 
| + | 
| +namespace bindings_internal | 
| +{ | 
| +  typedef void* TYPEID; | 
| + | 
| +  enum class TypeCategory | 
| +  { | 
| +    UNKNOWN, | 
| +    VOID, | 
| +    INT, | 
| +    DEPENDENT_STRING, | 
| +    OWNED_STRING, | 
| +    STRING_REF, | 
| +    CLASS_PTR | 
| +  }; | 
| + | 
| +  template<typename T> | 
| +  struct TypeInfo | 
| +  { | 
| +    static char c; | 
| +    constexpr operator TYPEID() const | 
| +    { | 
| +      return &c; | 
| +    } | 
| + | 
| +    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_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>()) | 
| +      { | 
| +        return TypeCategory::STRING_REF; | 
| +      } | 
| + | 
| +      if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>::type>()) | 
| +        return TypeCategory::CLASS_PTR; | 
| + | 
| +      return TypeCategory::UNKNOWN; | 
| +    } | 
| + | 
| +    TYPEID pointer_type() const | 
| +    { | 
| +      if (std::is_pointer<T>()) | 
| +        return TypeInfo<typename std::remove_pointer<T>::type>(); | 
| +      else | 
| +        return nullptr; | 
| +    } | 
| +  }; | 
| + | 
| +  template<typename T> | 
| +  char TypeInfo<T>::c; | 
| + | 
| +  struct FunctionInfo | 
| +  { | 
| +    TypeCategory returnType; | 
| +    TYPEID pointerType; | 
| +    std::vector<TypeCategory> args; | 
| +    bool instance_function; | 
| +    int effectiveArgs; | 
| +    TypeCategory effectiveReturnType; | 
| +    char name[1024]; | 
| + | 
| +    FunctionInfo() | 
| +    { | 
| +      name[0] = 0; | 
| +    } | 
| + | 
| +    FunctionInfo(TypeCategory returnType, TYPEID pointerType, | 
| +        std::initializer_list<TypeCategory> argTypes, bool instance_function, | 
| +        void* function) | 
| +        : returnType(returnType), pointerType(pointerType), | 
| +          instance_function(instance_function) | 
| +    { | 
| +      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; | 
| + | 
| +      for (auto it = argTypes.begin(); it != argTypes.end(); ++it) | 
| +      { | 
| +        if (*it != TypeCategory::INT && *it != TypeCategory::STRING_REF && | 
| +            *it != TypeCategory::CLASS_PTR) | 
| +        { | 
| +          throw std::runtime_error("Unexpected function argument type"); | 
| +        } | 
| +        args.push_back(*it); | 
| +      } | 
| + | 
| +      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"); | 
| +      } | 
| + | 
| +      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); | 
| +    } | 
| + | 
| +    template<typename ReturnType, typename... Args> | 
| +    FunctionInfo(ReturnType (*function)(Args...)) | 
| +        : FunctionInfo(TypeInfo<ReturnType>(), | 
| +          TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, | 
| +          &function) | 
| +    { | 
| +    } | 
| + | 
| +    template<typename ClassType, typename ReturnType, typename... Args> | 
| +    FunctionInfo(ReturnType (ClassType::*function)(Args...)) | 
| +        : FunctionInfo(TypeInfo<ReturnType>(), | 
| +          TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
| +          &function) | 
| +    { | 
| +    } | 
| + | 
| +    template<typename ClassType, typename ReturnType, typename... Args> | 
| +    FunctionInfo(ReturnType (ClassType::*function)(Args...) const) | 
| +        : FunctionInfo(TypeInfo<ReturnType>(), | 
| +          TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
| +          &function) | 
| +    { | 
| +    } | 
| + | 
| +    bool empty() const | 
| +    { | 
| +      return name[0] == 0; | 
| +    } | 
| + | 
| +    void get_function_name(void* ptr, int numArgs, bool voidResult) | 
| +    { | 
| +      // 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 args = []; | 
| +        for (var i = 0; i < $2; i++) | 
| +        { | 
| +          signature += "i"; | 
| +          args.push(0); | 
| +        } | 
| + | 
| +        var oldPrintErr = Module.printErr; | 
| +        var oldStackTrace = stackTrace; | 
| +        var sp = Runtime.stackSave(); | 
| +        Module.printErr = function(){}; | 
| +        stackTrace = function() | 
| +        { | 
| +          var stack = []; | 
| +          for (var f = arguments.callee.caller; f; f = f.caller) | 
| +          { | 
| +            if (f.name) | 
| +            { | 
| +              if (f.name.indexOf("dynCall") == 0) | 
| +                break; | 
| +              else | 
| +                stack.push(f.name); | 
| +            } | 
| +          } | 
| + | 
| +          result = stack[stack.length - 1]; | 
| +          if (result && result.indexOf("__wrapper") >= 0) | 
| +            result = stack[stack.length - 2]; | 
| +          throw result; | 
| +        }; | 
| + | 
| +        Runtime.stackRestore(STACK_MAX); | 
| + | 
| +        try | 
| +        { | 
| +          Runtime.dynCall(signature, HEAP32[$1 >> 2], args); | 
| +        } | 
| +        catch(e) | 
| +        { | 
| +          Module.stringToAscii(e, $0); | 
| +        } | 
| +        finally | 
| +        { | 
| +          Runtime.stackRestore(sp); | 
| +          Module.printErr = oldPrintErr; | 
| +          stackTrace = oldStackTrace; | 
| +        } | 
| +      }, name, ptr, numArgs, voidResult); | 
| +    } | 
| +  }; | 
| + | 
| +  class NoBaseClass | 
| +  { | 
| +  }; | 
| + | 
| +  struct PropertyInfo | 
| +  { | 
| +    std::string name; | 
| +    FunctionInfo getter; | 
| +    FunctionInfo setter; | 
| +    std::string jsValue; | 
| +  }; | 
| + | 
| +  struct MethodInfo | 
| +  { | 
| +    std::string name; | 
| +    FunctionInfo call; | 
| +  }; | 
| + | 
| +  struct DifferentiatorInfo | 
| +  { | 
| +    FunctionInfo call; | 
| +    std::vector<std::pair<int,std::string>> mapping; | 
| +  }; | 
| + | 
| +  struct ClassInfo | 
| +  { | 
| +    ClassInfo* baseClass; | 
| +    std::string name; | 
| +    std::vector<PropertyInfo> properties; | 
| +    std::vector<MethodInfo> methods; | 
| +    std::vector<FunctionInfo> initializers; | 
| +    DifferentiatorInfo subclass_differentiator; | 
| +  }; | 
| + | 
| +  std::map<TYPEID,ClassInfo> classes; | 
| + | 
| +  void register_class(const char* name, TYPEID classID, TYPEID baseClassID) | 
| +  { | 
| +    auto it = classes.find(classID); | 
| +    if (it != classes.end()) | 
| +      throw std::runtime_error(std::string("Duplicate definition for class ") + name); | 
| + | 
| +    ClassInfo* baseClass = nullptr; | 
| +    if (baseClassID != TypeInfo<NoBaseClass>()) | 
| +    { | 
| +      it = classes.find(baseClassID); | 
| +      if (it == classes.end()) | 
| +        throw std::runtime_error(std::string("Unknown base class defined for class ") + name); | 
| +      baseClass = &(it->second); | 
| +    } | 
| + | 
| +    ClassInfo classInfo; | 
| +    classInfo.baseClass = baseClass; | 
| +    classInfo.name = name; | 
| +    classes[classID] = classInfo; | 
| +  } | 
| + | 
| +  void register_property(TYPEID classID, const char* name, | 
| +      const FunctionInfo& getter, const FunctionInfo& setter, | 
| +      const char* jsValue = "") | 
| +  { | 
| +    auto it = classes.find(classID); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error(std::string("Property defined on unknown class: ") + name); | 
| + | 
| +    PropertyInfo propertyInfo; | 
| +    propertyInfo.name = name; | 
| +    propertyInfo.getter = getter; | 
| +    propertyInfo.setter = setter; | 
| +    propertyInfo.jsValue = jsValue; | 
| +    it->second.properties.push_back(propertyInfo); | 
| +  } | 
| + | 
| +  void register_method(TYPEID classID, const char* name, | 
| +      const FunctionInfo& call) | 
| +  { | 
| +    auto it = classes.find(classID); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error(std::string("Method defined on unknown class: ") + name); | 
| + | 
| +    MethodInfo methodInfo; | 
| +    methodInfo.name = name; | 
| +    methodInfo.call = call; | 
| +    it->second.methods.push_back(methodInfo); | 
| +  } | 
| + | 
| +  void register_initializer(TYPEID classID, const FunctionInfo& call) | 
| +  { | 
| +    auto it = classes.find(classID); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error("Initializer defined on unknown class"); | 
| + | 
| +    it->second.initializers.push_back(call); | 
| +  } | 
| + | 
| +  void register_differentiator(TYPEID classID, const FunctionInfo& call, | 
| +      std::vector<std::pair<int,std::string>>& mapping) | 
| +  { | 
| +    auto it = classes.find(classID); | 
| +    if (it == classes.end()) | 
| +      throw std::runtime_error("Subclass differentiator defined on unknown class"); | 
| + | 
| +    if (!it->second.subclass_differentiator.call.empty()) | 
| +      throw std::runtime_error("More than one subclass differentiator defined for class " + it->second.name); | 
| + | 
| +    DifferentiatorInfo differentiatorInfo; | 
| +    differentiatorInfo.call = call; | 
| +    differentiatorInfo.mapping = mapping; | 
| +    it->second.subclass_differentiator = differentiatorInfo; | 
| +  } | 
| + | 
| +  const std::string generateCall(const FunctionInfo& call, | 
| +      std::vector<std::string>& params) | 
| +  { | 
| +    if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| +        call.returnType == TypeCategory::OWNED_STRING) | 
| +    { | 
| +      params.insert(params.begin(), "string"); | 
| +    } | 
| + | 
| +    std::string call_str(call.name); | 
| +    call_str += "("; | 
| +    for (int i = 0; i < params.size(); i++) | 
| +    { | 
| +      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) | 
| +      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 = getStringData(string);\n"; | 
| +      if (call.returnType == TypeCategory::OWNED_STRING) | 
| +        result += "  Module._DestroyString(string);\n"; | 
| +      return result; | 
| +    } | 
| +    else if (call.returnType == TypeCategory::STRING_REF) | 
| +    { | 
| +      return "  var result = getStringData(" + call_str + ");\n"; | 
| +    } | 
| +    else if (call.returnType == TypeCategory::CLASS_PTR) | 
| +    { | 
| +      std::string result; | 
| +      result += "  var result = " + call_str + ";\n"; | 
| +      result += "  if (result)\n"; | 
| +      result += "  {\n"; | 
| +      result += "    Module._AddRef(result);\n"; | 
| + | 
| +      auto it = classes.find(call.pointerType); | 
| +      if (it == classes.end()) | 
| +        throw std::runtime_error("Function " + std::string(call.name) + " returns pointer to unknown class"); | 
| + | 
| +      const ClassInfo& cls = it->second; | 
| +      if (cls.subclass_differentiator.call.empty()) | 
| +        result += "    result = " + cls.name + "(result);\n"; | 
| +      else | 
| +      { | 
| +        result += "    var type = " + | 
| +            std::string(cls.subclass_differentiator.call.name) + "(result);\n"; | 
| +        result += "    if (type in " + cls.name + "_mapping)\n"; | 
| +        result += "      result = new (exports[" + cls.name + "_mapping[type]])(result);\n"; | 
| +        result += "    else\n"; | 
| +        result += "      throw new Error('Unexpected " + cls.name + " type: ' + type);\n"; | 
| +      } | 
| + | 
| +      result += "  }\n"; | 
| +      return result; | 
| +    } | 
| +    else | 
| +      throw std::runtime_error("Unexpected return type for " + std::string(call.name)); | 
| +  } | 
| + | 
| +  const std::string wrapCall(const FunctionInfo& call) | 
| +  { | 
| +    char buffer[20]; | 
| +    bool hasStringArgs = false; | 
| +    std::vector<std::string> params; | 
| +    std::string prefix = "function("; | 
| +    for (int i = 0; i < call.args.size(); i++) | 
| +    { | 
| +      sprintf(buffer, "arg%i", i); | 
| +      if (i > 0) | 
| +        prefix += ", "; | 
| +      prefix += buffer; | 
| + | 
| + | 
| +      if (call.args[i] == TypeCategory::STRING_REF) | 
| +      { | 
| +        hasStringArgs = true; | 
| +        params.push_back(std::string("createString(") + buffer + ")"); | 
| +      } | 
| +      else | 
| +        params.push_back(buffer); | 
| +    } | 
| +    prefix += ")\n{\n"; | 
| + | 
| +    std::string suffix = "}"; | 
| +    if (call.returnType != TypeCategory::VOID) | 
| +      suffix = "  return result;\n" + suffix; | 
| + | 
| +    if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
| +        call.returnType == TypeCategory::OWNED_STRING || hasStringArgs) | 
| +    { | 
| +      prefix += "  var sp = Runtime.stackSave();\n"; | 
| +      suffix = "  Runtime.stackRestore(sp);\n" + suffix; | 
| +    } | 
| + | 
| +    if (call.instance_function) | 
| +      params.insert(params.begin(), "this._pointer"); | 
| + | 
| +    return prefix + generateCall(call, params) + suffix; | 
| +  } | 
| + | 
| +  const std::string generatePropertyDescriptor(const PropertyInfo& property) | 
| +  { | 
| +    if (!property.jsValue.empty()) | 
| +      return "value: " + property.jsValue; | 
| + | 
| +    std::string result("get: " + wrapCall(property.getter)); | 
| +    if (!property.setter.empty()) | 
| +      result += ", set: " + wrapCall(property.setter); | 
| +    return result; | 
| +  } | 
| + | 
| +  void printHelpers() | 
| +  { | 
| +    printf("var sizeofString = %i\n", sizeof(String)); | 
| + | 
| +    puts(R"( | 
| +  function copyString(str, buffer) | 
| +  { | 
| +    var length = str.length; | 
| +    for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++) | 
| +      HEAP16[pointer] = str.charCodeAt(i); | 
| +    return length; | 
| +  } | 
| + | 
| +  function createString(str) | 
| +  { | 
| +    var length = 0; | 
| +    var buffer = 0; | 
| +    if (str) | 
| +    { | 
| +      buffer = Runtime.stackAlloc(str.length * 2); | 
| +      length = copyString(str, buffer); | 
| +    } | 
| + | 
| +    var result = Module.Runtime.stackAlloc(sizeofString); | 
| +    Module._InitString(result, buffer, length); | 
| +    return result; | 
| +  } | 
| + | 
| +  function getStringData(str) | 
| +  { | 
| +    var length = Module._GetStringLength(str); | 
| +    var pointer = Module._GetStringData(str) >> 1; | 
| +    return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + length)); | 
| +  } | 
| + | 
| +  function createClass(superclass) | 
| +  { | 
| +    var result = function(pointer) | 
| +    { | 
| +      this._pointer = pointer; | 
| +    }; | 
| +    if (superclass) | 
| +      result.prototype = Object.create(superclass.prototype); | 
| +    result.prototype.delete = function() | 
| +    { | 
| +      Module._ReleaseRef(this._pointer); | 
| +    }; | 
| +    return result; | 
| +  })"); | 
| +  } | 
| + | 
| +  void printClass(ClassInfo& cls) | 
| +  { | 
| +    DifferentiatorInfo differentiator = cls.subclass_differentiator; | 
| +    if (!differentiator.call.empty()) | 
| +    { | 
| +      printf("var %s_mapping = \n", cls.name.c_str()); | 
| +      puts("{"); | 
| +      for (auto it = differentiator.mapping.begin(); it != differentiator.mapping.end(); ++it) | 
| +        printf("  %i: '%s',\n", it->first, it->second.c_str()); | 
| +      puts("};"); | 
| +    } | 
| + | 
| +    printf("exports.%s = createClass(%s);\n", cls.name.c_str(), | 
| +        (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "")); | 
| + | 
| +    for (auto it = cls.properties.begin(); it != cls.properties.end(); ++it) | 
| +    { | 
| +      printf("Object.defineProperty(exports.%s.prototype, '%s', {%s});\n", | 
| +          cls.name.c_str(), it->name.c_str(), | 
| +          generatePropertyDescriptor(*it).c_str()); | 
| +    } | 
| + | 
| +    for (auto it = cls.methods.begin(); it != cls.methods.end(); ++it) | 
| +    { | 
| +      std::string obj("exports." + cls.name); | 
| +      if (it->call.instance_function) | 
| +        obj += ".prototype"; | 
| +      printf("%s.%s = %s;\n", obj.c_str(), it->name.c_str(), | 
| +          wrapCall(it->call).c_str()); | 
| +    } | 
| + | 
| +    for (auto it = cls.initializers.begin(); it != cls.initializers.end(); ++it) | 
| +      printf("%s()\n", it->name); | 
| +  } | 
| + | 
| +  void printBindings() | 
| +  { | 
| +    printHelpers(); | 
| + | 
| +    for (auto it = classes.begin(); it != classes.end(); ++it) | 
| +      printClass(it->second); | 
| +  } | 
| +} | 
| + | 
| +#if defined(PRINT_BINDINGS) | 
| +  // Bindings generation step: collect bindings information and print | 
| +  // corresponding JS code. | 
| +  #define EMSCRIPTEN_BINDINGS \ | 
| +      static struct BindingsInitializer {\ | 
| +          BindingsInitializer();\ | 
| +          BindingsInitializer(bool dummy)\ | 
| +          {\ | 
| +            try\ | 
| +            {\ | 
| +              BindingsInitializer();\ | 
| +              bindings_internal::printBindings();\ | 
| +            }\ | 
| +            catch (const std::exception& e)\ | 
| +            {\ | 
| +              EM_ASM_ARGS(\ | 
| +                console.error("Error occurred generating JavaScript bindings: " +\ | 
| +                    Module.AsciiToString($0)), e.what()\ | 
| +              );\ | 
| +              abort();\ | 
| +            }\ | 
| +          }\ | 
| +      } BindingsInitializer_instance(true);\ | 
| +      BindingsInitializer::BindingsInitializer() | 
| +#else | 
| +  // Actual compilation step: ignore bindings information but define some | 
| +  // exported helper functions necessary for the bindings. | 
| +  #define EMSCRIPTEN_BINDINGS \ | 
| +      extern "C"\ | 
| +      {\ | 
| +        void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ | 
| +            String::value_type* data, String::size_type len)\ | 
| +        {\ | 
| +          /* String is already allocated on stack, we merely need to call*/\ | 
| +          /* constructor.*/\ | 
| +          new (str) DependentString(data, len);\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\ | 
| +        {\ | 
| +          /* Stack memory will be freed automatically, we need to call*/\ | 
| +          /* destructor explicitly however.*/\ | 
| +          str->~OwnedString();\ | 
| +        }\ | 
| +        String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\ | 
| +            const String& str)\ | 
| +        {\ | 
| +          return str.length();\ | 
| +        }\ | 
| +        const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\ | 
| +            const String& str)\ | 
| +        {\ | 
| +          return str.data();\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE AddRef(ref_counted* ptr)\ | 
| +        {\ | 
| +          ptr->AddRef();\ | 
| +        }\ | 
| +        void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\ | 
| +        {\ | 
| +          ptr->ReleaseRef();\ | 
| +        }\ | 
| +      }\ | 
| +      void BindingsInitializer_dummy() | 
| +#endif | 
| + | 
| +template<typename ClassType, | 
| +    typename BaseClass = bindings_internal::NoBaseClass, | 
| +    typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::type* = nullptr> | 
| +class class_ | 
| +{ | 
| +public: | 
| +  class_(const char* name) | 
| +  { | 
| +    bindings_internal::register_class(name, | 
| +        bindings_internal::TypeInfo<ClassType>(), | 
| +        bindings_internal::TypeInfo<BaseClass>()); | 
| +  } | 
| + | 
| +  template<typename FieldType> | 
| +  const class_& property(const char* name, | 
| +      FieldType (ClassType::*getter)() const, | 
| +      void (ClassType::*setter)(FieldType) = 0) const | 
| +  { | 
| +    bindings_internal::register_property( | 
| +        bindings_internal::TypeInfo<ClassType>(), name, getter, setter); | 
| +    return *this; | 
| +  } | 
| + | 
| +  const class_& class_property(const char* name, | 
| +      const char* jsValue) const | 
| +  { | 
| +    bindings_internal::register_property( | 
| +        bindings_internal::TypeInfo<ClassType>(), name, | 
| +        bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(), | 
| +        jsValue); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& function(const char* name, ReturnType (ClassType::*method)(Args...)) const | 
| +  { | 
| +    bindings_internal::register_method( | 
| +        bindings_internal::TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& function(const char* name, ReturnType (ClassType::*method)(Args...) const) const | 
| +  { | 
| +    bindings_internal::register_method( | 
| +        bindings_internal::TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, typename... Args> | 
| +  const class_& class_function(const char* name, ReturnType (*method)(Args...)) const | 
| +  { | 
| +    bindings_internal::register_method( | 
| +        bindings_internal::TypeInfo<ClassType>(), name, method); | 
| +    return *this; | 
| +  } | 
| + | 
| +  const class_& class_initializer(void (*function)()) const | 
| +  { | 
| +    bindings_internal::register_initializer( | 
| +        bindings_internal::TypeInfo<ClassType>(), function); | 
| +    return *this; | 
| +  } | 
| + | 
| +  template<typename ReturnType, | 
| +      typename std::enable_if<std::is_convertible<ReturnType, int>::value>::type* = nullptr> | 
| +  const class_& subclass_differentiator(ReturnType (*function)(ClassType*), | 
| +      std::initializer_list<std::pair<ReturnType,const char*>> list) const | 
| +  { | 
| +    std::vector<std::pair<int,std::string>> mapping; | 
| +    for (auto it = list.begin(); it != list.end(); ++it) | 
| +      mapping.push_back(std::pair<int,std::string>(it->first, it->second)); | 
| + | 
| +    bindings_internal::register_differentiator( | 
| +        bindings_internal::TypeInfo<ClassType>(), function, mapping); | 
| +    return *this; | 
| +  } | 
| +}; | 
|  |