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