| LEFT | RIGHT | 
|---|
| (no file at all) |  | 
|  | 1 #pragma once | 
|  | 2 | 
|  | 3 #include <cstddef> | 
|  | 4 #include <cstdint> | 
|  | 5 #include <cstdio> | 
|  | 6 #include <cstdlib> | 
|  | 7 #include <exception> | 
|  | 8 #include <map> | 
|  | 9 #include <string> | 
|  | 10 #include <type_traits> | 
|  | 11 #include <utility> | 
|  | 12 #include <vector> | 
|  | 13 | 
|  | 14 #include <emscripten.h> | 
|  | 15 | 
|  | 16 #include "String.h" | 
|  | 17 #include "intrusive_ptr.h" | 
|  | 18 | 
|  | 19 namespace bindings_internal | 
|  | 20 { | 
|  | 21   typedef void* TYPEID; | 
|  | 22 | 
|  | 23   enum class TypeCategory | 
|  | 24   { | 
|  | 25     UNKNOWN, | 
|  | 26     VOID, | 
|  | 27     INT, | 
|  | 28     DEPENDENT_STRING, | 
|  | 29     OWNED_STRING, | 
|  | 30     STRING_REF, | 
|  | 31     CLASS_PTR | 
|  | 32   }; | 
|  | 33 | 
|  | 34   template<typename T> | 
|  | 35   struct TypeInfo | 
|  | 36   { | 
|  | 37     /* | 
|  | 38      * Since TypeInfo is a templated type, in practice the compiler will define | 
|  | 39      * a new type for each possible template parameter value. We use that fact | 
|  | 40      * to generate type identifiers: each of these TypeInfo types has a | 
|  | 41      * different s_typeIDHelper member, so we use a pointer to that static | 
|  | 42      * variable as a type identifier - it will be different for each template | 
|  | 43      * parameter. | 
|  | 44      */ | 
|  | 45     static char s_typeIDHelper; | 
|  | 46     constexpr operator TYPEID() const | 
|  | 47     { | 
|  | 48       return &s_typeIDHelper; | 
|  | 49     } | 
|  | 50 | 
|  | 51     constexpr operator TypeCategory() const | 
|  | 52     { | 
|  | 53       if (std::is_void<T>()) | 
|  | 54         return TypeCategory::VOID; | 
|  | 55 | 
|  | 56       if (std::is_integral<T>() || std::is_enum<T>()) | 
|  | 57         return TypeCategory::INT; | 
|  | 58 | 
|  | 59       if (std::is_same<DependentString, T>() || std::is_same<const DependentStri
     ng, T>()) | 
|  | 60         return TypeCategory::DEPENDENT_STRING; | 
|  | 61 | 
|  | 62       if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>()
     ) | 
|  | 63         return TypeCategory::OWNED_STRING; | 
|  | 64 | 
|  | 65       if (std::is_same<String&, T>() || std::is_same<const String&, T>() || | 
|  | 66         std::is_same<DependentString&, T>()) | 
|  | 67       { | 
|  | 68         return TypeCategory::STRING_REF; | 
|  | 69       } | 
|  | 70 | 
|  | 71       if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>:
     :type>()) | 
|  | 72         return TypeCategory::CLASS_PTR; | 
|  | 73 | 
|  | 74       return TypeCategory::UNKNOWN; | 
|  | 75     } | 
|  | 76 | 
|  | 77     constexpr TYPEID pointer_type() const | 
|  | 78     { | 
|  | 79       if (std::is_pointer<T>()) | 
|  | 80         return TypeInfo<typename std::remove_pointer<T>::type>(); | 
|  | 81       else | 
|  | 82         return nullptr; | 
|  | 83     } | 
|  | 84   }; | 
|  | 85 | 
|  | 86   template<typename T> | 
|  | 87   char TypeInfo<T>::s_typeIDHelper; | 
|  | 88 | 
|  | 89   struct FunctionInfo | 
|  | 90   { | 
|  | 91     TypeCategory returnType; | 
|  | 92     TYPEID pointerType; | 
|  | 93     std::vector<TypeCategory> args; | 
|  | 94     bool instance_function; | 
|  | 95     int effectiveArgs; | 
|  | 96     TypeCategory effectiveReturnType; | 
|  | 97     char name[1024]; | 
|  | 98 | 
|  | 99     FunctionInfo() | 
|  | 100     { | 
|  | 101       name[0] = '\0'; | 
|  | 102     } | 
|  | 103 | 
|  | 104     FunctionInfo(TypeCategory returnType, TYPEID pointerType, | 
|  | 105         std::initializer_list<TypeCategory> argTypes, bool instance_function, | 
|  | 106         void* function) | 
|  | 107         : returnType(returnType), pointerType(pointerType), | 
|  | 108           instance_function(instance_function) | 
|  | 109     { | 
|  | 110       name[0] = '\0'; | 
|  | 111 | 
|  | 112       // The function parameter is a pointer to the function pointer. | 
|  | 113       // Emscripten's "function pointers" are actually integers indicating the | 
|  | 114       // position in the call table. 0 represents nullptr. | 
|  | 115       if (!*reinterpret_cast<int*>(function)) | 
|  | 116         return; | 
|  | 117 | 
|  | 118       for (const auto& item : argTypes) | 
|  | 119       { | 
|  | 120         if (item != TypeCategory::INT && item != TypeCategory::STRING_REF && | 
|  | 121             item != TypeCategory::CLASS_PTR) | 
|  | 122         { | 
|  | 123           throw std::runtime_error("Unexpected function argument type"); | 
|  | 124         } | 
|  | 125         args.push_back(item); | 
|  | 126       } | 
|  | 127 | 
|  | 128       if (returnType != TypeCategory::VOID && returnType != TypeCategory::INT && | 
|  | 129           returnType != TypeCategory::DEPENDENT_STRING && | 
|  | 130           returnType != TypeCategory::OWNED_STRING && | 
|  | 131           returnType != TypeCategory::STRING_REF && | 
|  | 132           returnType != TypeCategory::CLASS_PTR) | 
|  | 133       { | 
|  | 134         throw std::runtime_error("Unexpected function return type"); | 
|  | 135       } | 
|  | 136 | 
|  | 137       effectiveArgs = args.size(); | 
|  | 138       effectiveReturnType = returnType; | 
|  | 139       if (instance_function) | 
|  | 140         effectiveArgs++; | 
|  | 141 | 
|  | 142       if (returnType == TypeCategory::DEPENDENT_STRING || | 
|  | 143           returnType == TypeCategory::OWNED_STRING) | 
|  | 144       { | 
|  | 145         effectiveArgs++; | 
|  | 146         effectiveReturnType = TypeCategory::VOID; | 
|  | 147       } | 
|  | 148 | 
|  | 149       get_function_name(function, effectiveArgs, | 
|  | 150           effectiveReturnType == TypeCategory::VOID); | 
|  | 151     } | 
|  | 152 | 
|  | 153     template<typename ReturnType, typename... Args> | 
|  | 154     FunctionInfo(ReturnType (*function)(Args...)) | 
|  | 155         : FunctionInfo(TypeInfo<ReturnType>(), | 
|  | 156           TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, | 
|  | 157           &function) | 
|  | 158     { | 
|  | 159     } | 
|  | 160 | 
|  | 161     template<typename ClassType, typename ReturnType, typename... Args> | 
|  | 162     FunctionInfo(ReturnType (ClassType::*function)(Args...)) | 
|  | 163         : FunctionInfo(TypeInfo<ReturnType>(), | 
|  | 164           TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
|  | 165           &function) | 
|  | 166     { | 
|  | 167     } | 
|  | 168 | 
|  | 169     template<typename ClassType, typename ReturnType, typename... Args> | 
|  | 170     FunctionInfo(ReturnType (ClassType::*function)(Args...) const) | 
|  | 171         : FunctionInfo(TypeInfo<ReturnType>(), | 
|  | 172           TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 
|  | 173           &function) | 
|  | 174     { | 
|  | 175     } | 
|  | 176 | 
|  | 177     bool empty() const | 
|  | 178     { | 
|  | 179       return name[0] == '\0'; | 
|  | 180     } | 
|  | 181 | 
|  | 182     void get_function_name(void* ptr, int numArgs, bool voidResult) | 
|  | 183     { | 
|  | 184       // This is a hack, C++ won't let us get the mangled function name. | 
|  | 185       // JavaScript is more dynamic so we pass the pointer to our function | 
|  | 186       // there. With that and the function signature we can call the function - | 
|  | 187       // with a full stack so that we will cause it to abort. Sometimes the | 
|  | 188       // function we are calling will also be missing from the build. The result | 
|  | 189       // is the same: abort() is called which in turn calls stackTrace(). By | 
|  | 190       // replacing stackTrace() we get access to the call stack and search it | 
|  | 191       // for the name of our function. | 
|  | 192 | 
|  | 193       EM_ASM_ARGS({ | 
|  | 194         var signature = $3 ? "v" : "i"; | 
|  | 195         var args = []; | 
|  | 196         for (var i = 0; i < $2; i++) | 
|  | 197         { | 
|  | 198           signature += "i"; | 
|  | 199           args.push(0); | 
|  | 200         } | 
|  | 201 | 
|  | 202         var oldPrint = Module.print; | 
|  | 203         var oldPrintErr = Module.printErr; | 
|  | 204         var oldStackTrace = stackTrace; | 
|  | 205         var sp = Runtime.stackSave(); | 
|  | 206         Module.print = function(){}; | 
|  | 207         Module.printErr = function(){}; | 
|  | 208         stackTrace = function() | 
|  | 209         { | 
|  | 210           var stack = []; | 
|  | 211           for (var f = arguments.callee.caller; f; f = f.caller) | 
|  | 212           { | 
|  | 213             if (f.name) | 
|  | 214             { | 
|  | 215               if (f.name.indexOf("dynCall") == 0) | 
|  | 216                 break; | 
|  | 217               else | 
|  | 218                 stack.push(f.name); | 
|  | 219             } | 
|  | 220           } | 
|  | 221 | 
|  | 222           result = stack[stack.length - 1]; | 
|  | 223           if (result && result.indexOf("__wrapper") >= 0) | 
|  | 224             result = stack[stack.length - 2]; | 
|  | 225           throw result; | 
|  | 226         }; | 
|  | 227 | 
|  | 228         Runtime.stackRestore(STACK_MAX); | 
|  | 229 | 
|  | 230         try | 
|  | 231         { | 
|  | 232           Runtime.dynCall(signature, HEAP32[$1 >> 2], args); | 
|  | 233         } | 
|  | 234         catch(e) | 
|  | 235         { | 
|  | 236           Module.stringToAscii(e, $0); | 
|  | 237         } | 
|  | 238         finally | 
|  | 239         { | 
|  | 240           Runtime.stackRestore(sp); | 
|  | 241           Module.print = oldPrint; | 
|  | 242           Module.printErr = oldPrintErr; | 
|  | 243           stackTrace = oldStackTrace; | 
|  | 244         } | 
|  | 245       }, name, ptr, numArgs, voidResult); | 
|  | 246     } | 
|  | 247   }; | 
|  | 248 | 
|  | 249   class NoBaseClass | 
|  | 250   { | 
|  | 251   }; | 
|  | 252 | 
|  | 253   struct PropertyInfo | 
|  | 254   { | 
|  | 255     std::string name; | 
|  | 256     FunctionInfo getter; | 
|  | 257     FunctionInfo setter; | 
|  | 258     std::string jsValue; | 
|  | 259   }; | 
|  | 260 | 
|  | 261   struct MethodInfo | 
|  | 262   { | 
|  | 263     std::string name; | 
|  | 264     FunctionInfo call; | 
|  | 265   }; | 
|  | 266 | 
|  | 267   struct DifferentiatorInfo | 
|  | 268   { | 
|  | 269     size_t offset; | 
|  | 270     std::vector<std::pair<int, std::string>> mapping; | 
|  | 271   }; | 
|  | 272 | 
|  | 273   struct ClassInfo | 
|  | 274   { | 
|  | 275     ClassInfo* baseClass; | 
|  | 276     std::string name; | 
|  | 277     std::vector<PropertyInfo> properties; | 
|  | 278     std::vector<MethodInfo> methods; | 
|  | 279     std::vector<FunctionInfo> initializers; | 
|  | 280     DifferentiatorInfo subclass_differentiator; | 
|  | 281     ptrdiff_t ref_counted_offset; | 
|  | 282   }; | 
|  | 283 | 
|  | 284   std::map<TYPEID, ClassInfo> classes; | 
|  | 285 | 
|  | 286   void register_class(const char* name, TYPEID classID, TYPEID baseClassID, | 
|  | 287                       ptrdiff_t ref_counted_offset) | 
|  | 288   { | 
|  | 289     auto it = classes.find(classID); | 
|  | 290     if (it != classes.end()) | 
|  | 291       throw std::runtime_error(std::string("Duplicate definition for class ") + 
     name); | 
|  | 292 | 
|  | 293     ClassInfo* baseClass = nullptr; | 
|  | 294     if (baseClassID != TypeInfo<NoBaseClass>()) | 
|  | 295     { | 
|  | 296       it = classes.find(baseClassID); | 
|  | 297       if (it == classes.end()) | 
|  | 298         throw std::runtime_error(std::string("Unknown base class defined for cla
     ss ") + name); | 
|  | 299       baseClass = &(it->second); | 
|  | 300     } | 
|  | 301 | 
|  | 302     ClassInfo classInfo; | 
|  | 303     classInfo.baseClass = baseClass; | 
|  | 304     classInfo.name = name; | 
|  | 305     classInfo.subclass_differentiator.offset = SIZE_MAX; | 
|  | 306     classInfo.ref_counted_offset = ref_counted_offset; | 
|  | 307     classes[classID] = classInfo; | 
|  | 308   } | 
|  | 309 | 
|  | 310   void register_property(TYPEID classID, const char* name, | 
|  | 311       const FunctionInfo& getter, const FunctionInfo& setter, | 
|  | 312       const char* jsValue = "") | 
|  | 313   { | 
|  | 314     auto it = classes.find(classID); | 
|  | 315     if (it == classes.end()) | 
|  | 316       throw std::runtime_error(std::string("Property defined on unknown class: "
     ) + name); | 
|  | 317 | 
|  | 318     PropertyInfo propertyInfo; | 
|  | 319     propertyInfo.name = name; | 
|  | 320     propertyInfo.getter = getter; | 
|  | 321     propertyInfo.setter = setter; | 
|  | 322     propertyInfo.jsValue = jsValue; | 
|  | 323     it->second.properties.push_back(propertyInfo); | 
|  | 324   } | 
|  | 325 | 
|  | 326   void register_method(TYPEID classID, const char* name, | 
|  | 327       const FunctionInfo& call) | 
|  | 328   { | 
|  | 329     auto it = classes.find(classID); | 
|  | 330     if (it == classes.end()) | 
|  | 331       throw std::runtime_error(std::string("Method defined on unknown class: ") 
     + name); | 
|  | 332 | 
|  | 333     MethodInfo methodInfo; | 
|  | 334     methodInfo.name = name; | 
|  | 335     methodInfo.call = call; | 
|  | 336     it->second.methods.push_back(methodInfo); | 
|  | 337   } | 
|  | 338 | 
|  | 339   void register_initializer(TYPEID classID, const FunctionInfo& call) | 
|  | 340   { | 
|  | 341     auto it = classes.find(classID); | 
|  | 342     if (it == classes.end()) | 
|  | 343       throw std::runtime_error("Initializer defined on unknown class"); | 
|  | 344 | 
|  | 345     it->second.initializers.push_back(call); | 
|  | 346   } | 
|  | 347 | 
|  | 348   void register_differentiator(TYPEID classID, size_t offset, | 
|  | 349       std::vector<std::pair<int, std::string>>& mapping) | 
|  | 350   { | 
|  | 351     auto it = classes.find(classID); | 
|  | 352     if (it == classes.end()) | 
|  | 353       throw std::runtime_error("Subclass differentiator defined on unknown class
     "); | 
|  | 354 | 
|  | 355     if (it->second.subclass_differentiator.offset != SIZE_MAX) | 
|  | 356       throw std::runtime_error("More than one subclass differentiator defined fo
     r class " + it->second.name); | 
|  | 357 | 
|  | 358     DifferentiatorInfo differentiatorInfo; | 
|  | 359     differentiatorInfo.offset = offset; | 
|  | 360     differentiatorInfo.mapping = mapping; | 
|  | 361     it->second.subclass_differentiator = differentiatorInfo; | 
|  | 362   } | 
|  | 363 | 
|  | 364   const std::string generateCall(const FunctionInfo& call, | 
|  | 365       std::vector<std::string>& params) | 
|  | 366   { | 
|  | 367     if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
|  | 368         call.returnType == TypeCategory::OWNED_STRING) | 
|  | 369     { | 
|  | 370       params.insert(params.begin(), "string"); | 
|  | 371     } | 
|  | 372 | 
|  | 373     std::string call_str(call.name); | 
|  | 374     call_str += "("; | 
|  | 375     for (int i = 0; i < params.size(); i++) | 
|  | 376     { | 
|  | 377       if (i > 0) | 
|  | 378         call_str += ", "; | 
|  | 379       call_str += params[i]; | 
|  | 380     } | 
|  | 381     call_str += ")"; | 
|  | 382 | 
|  | 383     if (call.returnType == TypeCategory::VOID) | 
|  | 384       return "  " + call_str + ";\n"; | 
|  | 385     else if (call.returnType == TypeCategory::INT) | 
|  | 386       return "  var result = " + call_str + ";\n"; | 
|  | 387     else if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
|  | 388         call.returnType == TypeCategory::OWNED_STRING) | 
|  | 389     { | 
|  | 390       std::string result; | 
|  | 391       result += "  var string = createString();\n"; | 
|  | 392       result += "  " + call_str + ";\n"; | 
|  | 393       result += "  var result = readString(string);\n"; | 
|  | 394       if (call.returnType == TypeCategory::OWNED_STRING) | 
|  | 395         result += "  Module._DestroyString(string);\n"; | 
|  | 396       return result; | 
|  | 397     } | 
|  | 398     else if (call.returnType == TypeCategory::STRING_REF) | 
|  | 399     { | 
|  | 400       return "  var result = readString(" + call_str + ");\n"; | 
|  | 401     } | 
|  | 402     else if (call.returnType == TypeCategory::CLASS_PTR) | 
|  | 403     { | 
|  | 404       std::string result; | 
|  | 405       result += "  var result = " + call_str + ";\n"; | 
|  | 406       result += "  if (result)\n"; | 
|  | 407       result += "  {\n"; | 
|  | 408 | 
|  | 409       auto it = classes.find(call.pointerType); | 
|  | 410       if (it == classes.end()) | 
|  | 411         throw std::runtime_error("Function " + std::string(call.name) + " return
     s pointer to unknown class"); | 
|  | 412 | 
|  | 413       const ClassInfo& cls = it->second; | 
|  | 414       auto offset = cls.subclass_differentiator.offset; | 
|  | 415       if (offset == SIZE_MAX) | 
|  | 416         result += "    result = " + cls.name + "(result);\n"; | 
|  | 417       else | 
|  | 418       { | 
|  | 419         result += "    var type = HEAP32[result + " + std::to_string(offset)+ " 
     >> 2];\n"; | 
|  | 420         result += "    if (type in " + cls.name + "_mapping)\n"; | 
|  | 421         result += "      result = new (exports[" + cls.name + "_mapping[type]])(
     result);\n"; | 
|  | 422         result += "    else\n"; | 
|  | 423         result += "      throw new Error('Unexpected " + cls.name + " type: ' + 
     type);\n"; | 
|  | 424       } | 
|  | 425 | 
|  | 426       result += "  }\n"; | 
|  | 427       return result; | 
|  | 428     } | 
|  | 429     else | 
|  | 430       throw std::runtime_error("Unexpected return type for " + std::string(call.
     name)); | 
|  | 431   } | 
|  | 432 | 
|  | 433   const std::string wrapCall(const FunctionInfo& call) | 
|  | 434   { | 
|  | 435     char buffer[20]; | 
|  | 436     bool hasStringArgs = false; | 
|  | 437     std::vector<std::string> params; | 
|  | 438     std::string prefix = "function("; | 
|  | 439     for (int i = 0; i < call.args.size(); i++) | 
|  | 440     { | 
|  | 441       sprintf(buffer, "arg%i", i); | 
|  | 442       if (i > 0) | 
|  | 443         prefix += ", "; | 
|  | 444       prefix += buffer; | 
|  | 445 | 
|  | 446       if (call.args[i] == TypeCategory::STRING_REF) | 
|  | 447       { | 
|  | 448         hasStringArgs = true; | 
|  | 449         params.push_back(std::string("createString(") + buffer + ")"); | 
|  | 450       } | 
|  | 451       else | 
|  | 452         params.push_back(buffer); | 
|  | 453     } | 
|  | 454     prefix += ")\n{\n"; | 
|  | 455 | 
|  | 456     std::string suffix = "}"; | 
|  | 457     if (call.returnType != TypeCategory::VOID) | 
|  | 458       suffix = "  return result;\n" + suffix; | 
|  | 459 | 
|  | 460     if (call.returnType == TypeCategory::DEPENDENT_STRING || | 
|  | 461         call.returnType == TypeCategory::OWNED_STRING || hasStringArgs) | 
|  | 462     { | 
|  | 463       prefix += "  var sp = Runtime.stackSave();\n"; | 
|  | 464       suffix = "  Runtime.stackRestore(sp);\n" + suffix; | 
|  | 465     } | 
|  | 466 | 
|  | 467     if (call.instance_function) | 
|  | 468       params.insert(params.begin(), "this._pointer"); | 
|  | 469 | 
|  | 470     return prefix + generateCall(call, params) + suffix; | 
|  | 471   } | 
|  | 472 | 
|  | 473   std::string generatePropertyDescriptor(const PropertyInfo& property) | 
|  | 474   { | 
|  | 475     if (!property.jsValue.empty()) | 
|  | 476       return "value: " + property.jsValue; | 
|  | 477 | 
|  | 478     std::string result("get: " + wrapCall(property.getter)); | 
|  | 479     if (!property.setter.empty()) | 
|  | 480       result += ", set: " + wrapCall(property.setter); | 
|  | 481     return result; | 
|  | 482   } | 
|  | 483 | 
|  | 484   void printHelpers() | 
|  | 485   { | 
|  | 486     printf("var sizeofString = %i;\n", sizeof(String)); | 
|  | 487 | 
|  | 488     puts(R"( | 
|  | 489   function copyString(str, buffer) | 
|  | 490   { | 
|  | 491     var length = str.length; | 
|  | 492     for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++) | 
|  | 493       HEAP16[pointer] = str.charCodeAt(i); | 
|  | 494     return length; | 
|  | 495   } | 
|  | 496 | 
|  | 497   function createString(str) | 
|  | 498   { | 
|  | 499     var length = 0; | 
|  | 500     var buffer = 0; | 
|  | 501     if (str) | 
|  | 502     { | 
|  | 503       buffer = Runtime.stackAlloc(str.length * 2); | 
|  | 504       length = copyString(str, buffer); | 
|  | 505     } | 
|  | 506 | 
|  | 507     var result = Module.Runtime.stackAlloc(sizeofString); | 
|  | 508     Module._InitString(result, buffer, length); | 
|  | 509     return result; | 
|  | 510   } | 
|  | 511 | 
|  | 512   function readString(str) | 
|  | 513   { | 
|  | 514     var length = Module._GetStringLength(str); | 
|  | 515     var pointer = Module._GetStringData(str) >> 1; | 
|  | 516     return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + len
     gth)); | 
|  | 517   } | 
|  | 518 | 
|  | 519   function createClass(superclass, ref_counted_offset) | 
|  | 520   { | 
|  | 521     var result = function(pointer) | 
|  | 522     { | 
|  | 523       this._pointer = pointer; | 
|  | 524     }; | 
|  | 525     if (superclass) | 
|  | 526       result.prototype = Object.create(superclass.prototype); | 
|  | 527     result.prototype.delete = function() | 
|  | 528     { | 
|  | 529       Module._ReleaseRef(this._pointer + ref_counted_offset); | 
|  | 530     }; | 
|  | 531     return result; | 
|  | 532   })"); | 
|  | 533   } | 
|  | 534 | 
|  | 535   void printClass(const ClassInfo& cls) | 
|  | 536   { | 
|  | 537     DifferentiatorInfo differentiator = cls.subclass_differentiator; | 
|  | 538     if (differentiator.offset != SIZE_MAX) | 
|  | 539     { | 
|  | 540       printf("var %s_mapping = \n", cls.name.c_str()); | 
|  | 541       puts("{"); | 
|  | 542       for (const auto& item : differentiator.mapping) | 
|  | 543         printf("  %i: '%s',\n", item.first, item.second.c_str()); | 
|  | 544       puts("};"); | 
|  | 545     } | 
|  | 546 | 
|  | 547     printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), | 
|  | 548         (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "null"), | 
|  | 549         cls.ref_counted_offset); | 
|  | 550 | 
|  | 551     for (const auto& item : cls.properties) | 
|  | 552     { | 
|  | 553       printf("Object.defineProperty(exports.%s.prototype, '%s', {%s});\n", | 
|  | 554           cls.name.c_str(), item.name.c_str(), | 
|  | 555           generatePropertyDescriptor(item).c_str()); | 
|  | 556     } | 
|  | 557 | 
|  | 558     for (const auto& item : cls.methods) | 
|  | 559     { | 
|  | 560       std::string obj("exports." + cls.name); | 
|  | 561       if (item.call.instance_function) | 
|  | 562         obj += ".prototype"; | 
|  | 563       printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), | 
|  | 564           wrapCall(item.call).c_str()); | 
|  | 565     } | 
|  | 566 | 
|  | 567     for (const auto& item : cls.initializers) | 
|  | 568       printf("%s()\n", item.name); | 
|  | 569   } | 
|  | 570 | 
|  | 571   void printBindings() | 
|  | 572   { | 
|  | 573     printHelpers(); | 
|  | 574 | 
|  | 575     for (const auto& item : classes) | 
|  | 576       printClass(item.second); | 
|  | 577   } | 
|  | 578 } | 
|  | 579 | 
|  | 580 #if defined(PRINT_BINDINGS) | 
|  | 581   // Bindings generation step: collect bindings information and print | 
|  | 582   // corresponding JS code. | 
|  | 583   #define EMSCRIPTEN_BINDINGS \ | 
|  | 584       struct BindingsInitializer {\ | 
|  | 585           BindingsInitializer();\ | 
|  | 586           BindingsInitializer(bool dummy)\ | 
|  | 587           {\ | 
|  | 588             try\ | 
|  | 589             {\ | 
|  | 590               BindingsInitializer();\ | 
|  | 591               bindings_internal::printBindings();\ | 
|  | 592             }\ | 
|  | 593             catch (const std::exception& e)\ | 
|  | 594             {\ | 
|  | 595               EM_ASM_ARGS(\ | 
|  | 596                 console.error("Error occurred generating JavaScript bindings: " 
     +\ | 
|  | 597                     Module.AsciiToString($0)), e.what()\ | 
|  | 598               );\ | 
|  | 599               abort();\ | 
|  | 600             }\ | 
|  | 601           }\ | 
|  | 602       } BindingsInitializer_instance(true);\ | 
|  | 603       BindingsInitializer::BindingsInitializer() | 
|  | 604 #else | 
|  | 605   // Actual compilation step: ignore bindings information but define some | 
|  | 606   // exported helper functions necessary for the bindings. | 
|  | 607   #define EMSCRIPTEN_BINDINGS \ | 
|  | 608       extern "C"\ | 
|  | 609       {\ | 
|  | 610         void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ | 
|  | 611             String::value_type* data, String::size_type len)\ | 
|  | 612         {\ | 
|  | 613           /* String is already allocated on stack, we merely need to call*/\ | 
|  | 614           /* constructor.*/\ | 
|  | 615           new (str) DependentString(data, len);\ | 
|  | 616         }\ | 
|  | 617         void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\ | 
|  | 618         {\ | 
|  | 619           /* Stack memory will be freed automatically, we need to call*/\ | 
|  | 620           /* destructor explicitly however.*/\ | 
|  | 621           str->~OwnedString();\ | 
|  | 622         }\ | 
|  | 623         String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\ | 
|  | 624             const String& str)\ | 
|  | 625         {\ | 
|  | 626           return str.length();\ | 
|  | 627         }\ | 
|  | 628         const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\ | 
|  | 629             const String& str)\ | 
|  | 630         {\ | 
|  | 631           return str.data();\ | 
|  | 632         }\ | 
|  | 633         void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\ | 
|  | 634         {\ | 
|  | 635           ptr->ReleaseRef();\ | 
|  | 636         }\ | 
|  | 637       }\ | 
|  | 638       void BindingsInitializer_dummy() | 
|  | 639 #endif | 
|  | 640 | 
|  | 641 template<typename ClassType, | 
|  | 642     typename BaseClass = bindings_internal::NoBaseClass, | 
|  | 643     typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::typ
     e* = nullptr> | 
|  | 644 class class_ | 
|  | 645 { | 
|  | 646 public: | 
|  | 647   class_(const char* name) | 
|  | 648   { | 
|  | 649     ClassType* ptr = reinterpret_cast<ClassType*>(0x10000000); | 
|  | 650     ptrdiff_t ref_counted_offset = | 
|  | 651         reinterpret_cast<char*>(static_cast<ref_counted*>(ptr)) - | 
|  | 652         reinterpret_cast<char*>(ptr); | 
|  | 653     bindings_internal::register_class(name, | 
|  | 654         bindings_internal::TypeInfo<ClassType>(), | 
|  | 655         bindings_internal::TypeInfo<BaseClass>(), | 
|  | 656         ref_counted_offset | 
|  | 657       ); | 
|  | 658   } | 
|  | 659 | 
|  | 660   template<typename FieldType> | 
|  | 661   const class_& property(const char* name, | 
|  | 662       FieldType (ClassType::*getter)() const, | 
|  | 663       void (ClassType::*setter)(FieldType) = nullptr) const | 
|  | 664   { | 
|  | 665     bindings_internal::register_property( | 
|  | 666         bindings_internal::TypeInfo<ClassType>(), name, getter, setter); | 
|  | 667     return *this; | 
|  | 668   } | 
|  | 669 | 
|  | 670   const class_& class_property(const char* name, | 
|  | 671       const char* jsValue) const | 
|  | 672   { | 
|  | 673     bindings_internal::register_property( | 
|  | 674         bindings_internal::TypeInfo<ClassType>(), name, | 
|  | 675         bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(), | 
|  | 676         jsValue); | 
|  | 677     return *this; | 
|  | 678   } | 
|  | 679 | 
|  | 680   template<typename ReturnType, typename... Args> | 
|  | 681   const class_& function(const char* name, ReturnType (ClassType::*method)(Args.
     ..)) const | 
|  | 682   { | 
|  | 683     bindings_internal::register_method( | 
|  | 684         bindings_internal::TypeInfo<ClassType>(), name, method); | 
|  | 685     return *this; | 
|  | 686   } | 
|  | 687 | 
|  | 688   template<typename ReturnType, typename... Args> | 
|  | 689   const class_& function(const char* name, ReturnType (ClassType::*method)(Args.
     ..) const) const | 
|  | 690   { | 
|  | 691     bindings_internal::register_method( | 
|  | 692         bindings_internal::TypeInfo<ClassType>(), name, method); | 
|  | 693     return *this; | 
|  | 694   } | 
|  | 695 | 
|  | 696   template<typename ReturnType, typename... Args> | 
|  | 697   const class_& class_function(const char* name, ReturnType (*method)(Args...)) 
     const | 
|  | 698   { | 
|  | 699     bindings_internal::register_method( | 
|  | 700         bindings_internal::TypeInfo<ClassType>(), name, method); | 
|  | 701     return *this; | 
|  | 702   } | 
|  | 703 | 
|  | 704   const class_& class_initializer(void (*function)()) const | 
|  | 705   { | 
|  | 706     bindings_internal::register_initializer( | 
|  | 707         bindings_internal::TypeInfo<ClassType>(), function); | 
|  | 708     return *this; | 
|  | 709   } | 
|  | 710 | 
|  | 711   template<typename ReturnType, | 
|  | 712       typename std::enable_if<std::is_convertible<ReturnType, int32_t>::value>::
     type* = nullptr> | 
|  | 713   const class_& subclass_differentiator(ReturnType ClassType::* member, | 
|  | 714       std::initializer_list<std::pair<ReturnType, const char*>> list) const | 
|  | 715   { | 
|  | 716     ClassType* instance = nullptr; | 
|  | 717     size_t offset = (char*)&(instance->*member) - (char*)instance; | 
|  | 718 | 
|  | 719     std::vector<std::pair<int, std::string>> mapping; | 
|  | 720     for (const auto& item : list) | 
|  | 721       mapping.emplace_back(item.first, item.second); | 
|  | 722 | 
|  | 723     bindings_internal::register_differentiator( | 
|  | 724         bindings_internal::TypeInfo<ClassType>(), offset, mapping); | 
|  | 725     return *this; | 
|  | 726   } | 
|  | 727 }; | 
| LEFT | RIGHT | 
|---|