 Issue 29333474:
  Issue 4125 - [emscripten] Convert filter classes to C++  (Closed)
    
  
    Issue 29333474:
  Issue 4125 - [emscripten] Convert filter classes to C++  (Closed) 
  | Index: compiled/bindings.ipp | 
| =================================================================== | 
| new file mode 100644 | 
| --- /dev/null | 
| +++ b/compiled/bindings.ipp | 
| @@ -0,0 +1,710 @@ | 
| +#pragma once | 
| 
sergei
2016/06/16 21:16:53
Why does it not print 'use strict;'?
 
sergei
2016/06/16 21:16:57
Why are "-1" at the beginning of compiled/bindings
 
sergei
2016/06/16 21:17:08
I don't think that it is worth paying attention to
 
Wladimir Palant
2016/12/06 10:47:55
The bindings are only a small part of the resultin
 
Wladimir Palant
2016/12/06 10:48:12
That's output from an abort() call not being suppr
 | 
| + | 
| +#include <cstdio> | 
| +#include <cstdlib> | 
| +#include <cstring> | 
| +#include <exception> | 
| +#include <map> | 
| +#include <string> | 
| +#include <type_traits> | 
| +#include <utility> | 
| +#include <vector> | 
| 
sergei
2016/06/16 21:16:50
What do you think about excluding std classes from
 
Wladimir Palant
2016/12/06 10:48:00
Headers don't result in any code, they are merely
 | 
| + | 
| +#include <emscripten.h> | 
| + | 
| +#include "String.h" | 
| +#include "intrusive_ptr.h" | 
| + | 
| +namespace bindings_internal | 
| 
sergei
2016/06/16 21:17:05
It would be good to have some comment here which e
 
Wladimir Palant
2016/12/06 10:47:52
With the approach changing with each iteration, I
 | 
| +{ | 
| + 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; | 
| 
sergei
2016/06/16 21:17:10
I think it would be better to call it something li
 
Wladimir Palant
2016/12/06 10:47:59
Done.
 | 
| + 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 | 
| 
sergei
2016/06/16 21:16:51
Actually it might be makes sense to make the metho
 
Wladimir Palant
2016/12/06 10:47:42
Done.
 | 
| + { | 
| + 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; | 
| 
sergei
2016/06/16 21:17:02
Although it makes no difference I personally prefe
 
Wladimir Palant
2016/12/06 10:47:39
Done.
 | 
| + } | 
| + | 
| + 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 oldPrint = Module.print; | 
| + var oldPrintErr = Module.printErr; | 
| + var oldStackTrace = stackTrace; | 
| + var sp = Runtime.stackSave(); | 
| + Module.print = function(){}; | 
| + 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.print = oldPrint; | 
| + 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; | 
| + | 
| 
sergei
2016/06/16 21:16:45
Additional line.
 
Wladimir Palant
2016/12/06 10:47:57
Done.
 | 
| + | 
| + 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) | 
| 
sergei
2016/06/16 21:16:56
I'm not sure that we need the `const` for the retu
 
Wladimir Palant
2016/12/06 10:48:05
Done.
 | 
| + { | 
| + 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)); | 
| 
sergei
2016/06/16 21:17:16
What about ';' after "%i"?
 
Wladimir Palant
2016/12/06 10:48:07
Done.
 | 
| + | 
| + 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) | 
| 
sergei
2016/06/16 21:17:15
What do you think about renaming of this method. C
 
Wladimir Palant
2016/12/06 10:48:10
Ok, readString() it is - hopefully more obvious.
 | 
| + { | 
| + var length = Module._GetStringLength(str); | 
| + var pointer = Module._GetStringData(str) >> 1; | 
| + return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + length)); | 
| 
sergei
2016/06/16 21:16:46
Shouldn't we use fromCodePoint instead of fromChar
 
Wladimir Palant
2016/12/06 10:48:08
No, we shouldn't - we are using 16-bit characters.
 | 
| + } | 
| + | 
| + function createClass(superclass) | 
| + { | 
| + var result = function(pointer) | 
| + { | 
| + this._pointer = pointer; | 
| 
sergei
2016/06/16 21:17:11
We should call Module._AddRef on stored _pointer h
 
Wladimir Palant
2016/12/06 10:47:41
No, we shouldn't - that's a raw pointer we got her
 | 
| + }; | 
| + 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 {\ | 
| 
sergei
2016/06/16 21:16:54
I think static is not required here.
 
Wladimir Palant
2016/12/06 10:47:44
Done.
 | 
| + 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 | 
| 
sergei
2016/06/16 21:17:07
I would rather use nullptr instead of 0.
 
Wladimir Palant
2016/12/06 10:47:50
Done.
 | 
| + { | 
| + 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 | 
| 
sergei
2016/06/16 21:17:04
Just wonder, why are no spaces between types for s
 
Wladimir Palant
2016/12/06 10:47:54
Done.
 | 
| + { | 
| + 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)); | 
| 
sergei
2016/06/16 21:16:59
Merely a shorter syntax, here and above we can use
 
Wladimir Palant
2016/12/06 10:47:48
Done.
 | 
| + | 
| + bindings_internal::register_differentiator( | 
| + bindings_internal::TypeInfo<ClassType>(), function, mapping); | 
| + return *this; | 
| + } | 
| +}; |