Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: compiled/bindings.ipp

Issue 29333474: Issue 4125 - [emscripten] Convert filter classes to C++ (Closed)
Patch Set: Addressed comments from Patch Set 26 Created March 13, 2017, 5:36 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « compiled/bindings.cpp ('k') | compiled/debug.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 };
281
282 std::map<TYPEID, ClassInfo> classes;
283
284 void register_class(const char* name, TYPEID classID, TYPEID baseClassID)
285 {
286 auto it = classes.find(classID);
287 if (it != classes.end())
288 throw std::runtime_error(std::string("Duplicate definition for class ") + name);
289
290 ClassInfo* baseClass = nullptr;
291 if (baseClassID != TypeInfo<NoBaseClass>())
292 {
293 it = classes.find(baseClassID);
294 if (it == classes.end())
295 throw std::runtime_error(std::string("Unknown base class defined for cla ss ") + name);
296 baseClass = &(it->second);
297 }
298
299 ClassInfo classInfo;
300 classInfo.baseClass = baseClass;
301 classInfo.name = name;
302 classInfo.subclass_differentiator.offset = SIZE_MAX;
303 classes[classID] = classInfo;
304 }
305
306 void register_property(TYPEID classID, const char* name,
307 const FunctionInfo& getter, const FunctionInfo& setter,
308 const char* jsValue = "")
309 {
310 auto it = classes.find(classID);
311 if (it == classes.end())
312 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name);
313
314 PropertyInfo propertyInfo;
315 propertyInfo.name = name;
316 propertyInfo.getter = getter;
317 propertyInfo.setter = setter;
318 propertyInfo.jsValue = jsValue;
319 it->second.properties.push_back(propertyInfo);
320 }
321
322 void register_method(TYPEID classID, const char* name,
323 const FunctionInfo& call)
324 {
325 auto it = classes.find(classID);
326 if (it == classes.end())
327 throw std::runtime_error(std::string("Method defined on unknown class: ") + name);
328
329 MethodInfo methodInfo;
330 methodInfo.name = name;
331 methodInfo.call = call;
332 it->second.methods.push_back(methodInfo);
333 }
334
335 void register_initializer(TYPEID classID, const FunctionInfo& call)
336 {
337 auto it = classes.find(classID);
338 if (it == classes.end())
339 throw std::runtime_error("Initializer defined on unknown class");
340
341 it->second.initializers.push_back(call);
342 }
343
344 void register_differentiator(TYPEID classID, size_t offset,
345 std::vector<std::pair<int, std::string>>& mapping)
346 {
347 auto it = classes.find(classID);
348 if (it == classes.end())
349 throw std::runtime_error("Subclass differentiator defined on unknown class ");
350
351 if (it->second.subclass_differentiator.offset != SIZE_MAX)
352 throw std::runtime_error("More than one subclass differentiator defined fo r class " + it->second.name);
353
354 DifferentiatorInfo differentiatorInfo;
355 differentiatorInfo.offset = offset;
356 differentiatorInfo.mapping = mapping;
357 it->second.subclass_differentiator = differentiatorInfo;
358 }
359
360 const std::string generateCall(const FunctionInfo& call,
361 std::vector<std::string>& params)
362 {
363 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
364 call.returnType == TypeCategory::OWNED_STRING)
365 {
366 params.insert(params.begin(), "string");
367 }
368
369 std::string call_str(call.name);
370 call_str += "(";
371 for (int i = 0; i < params.size(); i++)
372 {
373 if (i > 0)
374 call_str += ", ";
375 call_str += params[i];
376 }
377 call_str += ")";
378
379 if (call.returnType == TypeCategory::VOID)
380 return " " + call_str + ";\n";
381 else if (call.returnType == TypeCategory::INT)
382 return " var result = " + call_str + ";\n";
383 else if (call.returnType == TypeCategory::DEPENDENT_STRING ||
384 call.returnType == TypeCategory::OWNED_STRING)
385 {
386 std::string result;
387 result += " var string = createString();\n";
388 result += " " + call_str + ";\n";
389 result += " var result = readString(string);\n";
390 if (call.returnType == TypeCategory::OWNED_STRING)
391 result += " Module._DestroyString(string);\n";
392 return result;
393 }
394 else if (call.returnType == TypeCategory::STRING_REF)
395 {
396 return " var result = readString(" + call_str + ");\n";
397 }
398 else if (call.returnType == TypeCategory::CLASS_PTR)
399 {
400 std::string result;
401 result += " var result = " + call_str + ";\n";
402 result += " if (result)\n";
403 result += " {\n";
404
405 auto it = classes.find(call.pointerType);
406 if (it == classes.end())
407 throw std::runtime_error("Function " + std::string(call.name) + " return s pointer to unknown class");
408
409 const ClassInfo& cls = it->second;
410 auto offset = cls.subclass_differentiator.offset;
411 if (offset == SIZE_MAX)
412 result += " result = " + cls.name + "(result);\n";
413 else
414 {
415 result += " var type = HEAP32[result + " + std::to_string(offset)+ " >> 2];\n";
416 result += " if (type in " + cls.name + "_mapping)\n";
417 result += " result = new (exports[" + cls.name + "_mapping[type]])( result);\n";
418 result += " else\n";
419 result += " throw new Error('Unexpected " + cls.name + " type: ' + type);\n";
420 }
421
422 result += " }\n";
423 return result;
424 }
425 else
426 throw std::runtime_error("Unexpected return type for " + std::string(call. name));
427 }
428
429 const std::string wrapCall(const FunctionInfo& call)
430 {
431 char buffer[20];
432 bool hasStringArgs = false;
433 std::vector<std::string> params;
434 std::string prefix = "function(";
435 for (int i = 0; i < call.args.size(); i++)
436 {
437 sprintf(buffer, "arg%i", i);
438 if (i > 0)
439 prefix += ", ";
440 prefix += buffer;
441
442 if (call.args[i] == TypeCategory::STRING_REF)
443 {
444 hasStringArgs = true;
445 params.push_back(std::string("createString(") + buffer + ")");
446 }
447 else
448 params.push_back(buffer);
449 }
450 prefix += ")\n{\n";
451
452 std::string suffix = "}";
453 if (call.returnType != TypeCategory::VOID)
454 suffix = " return result;\n" + suffix;
455
456 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
457 call.returnType == TypeCategory::OWNED_STRING || hasStringArgs)
458 {
459 prefix += " var sp = Runtime.stackSave();\n";
460 suffix = " Runtime.stackRestore(sp);\n" + suffix;
461 }
462
463 if (call.instance_function)
464 params.insert(params.begin(), "this._pointer");
465
466 return prefix + generateCall(call, params) + suffix;
467 }
468
469 std::string generatePropertyDescriptor(const PropertyInfo& property)
470 {
471 if (!property.jsValue.empty())
472 return "value: " + property.jsValue;
473
474 std::string result("get: " + wrapCall(property.getter));
475 if (!property.setter.empty())
476 result += ", set: " + wrapCall(property.setter);
477 return result;
478 }
479
480 void printHelpers()
481 {
482 printf("var sizeofString = %i;\n", sizeof(String));
483
484 puts(R"(
485 function copyString(str, buffer)
486 {
487 var length = str.length;
488 for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++)
489 HEAP16[pointer] = str.charCodeAt(i);
490 return length;
491 }
492
493 function createString(str)
494 {
495 var length = 0;
496 var buffer = 0;
497 if (str)
498 {
499 buffer = Runtime.stackAlloc(str.length * 2);
500 length = copyString(str, buffer);
501 }
502
503 var result = Module.Runtime.stackAlloc(sizeofString);
504 Module._InitString(result, buffer, length);
505 return result;
506 }
507
508 function readString(str)
509 {
510 var length = Module._GetStringLength(str);
511 var pointer = Module._GetStringData(str) >> 1;
512 return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + len gth));
513 }
514
515 function createClass(superclass)
516 {
517 var result = function(pointer)
518 {
519 this._pointer = pointer;
520 };
521 if (superclass)
522 result.prototype = Object.create(superclass.prototype);
523 result.prototype.delete = function()
524 {
525 // This is dangerous because this._pointer is a pointer to SomeClass,
526 // yet ReleaseRef expects ref_counted*. Even with SomeClass inheriting
527 // from ref_counted, the numerical pointer values aren't necessarily
528 // identical in C++.
529 Module._ReleaseRef(this._pointer);
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);\n", cls.name.c_str(),
548 (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : ""));
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 AddRef(ref_counted* ptr)\
633 {\
634 ptr->AddRef();\
635 }\
636 void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\
637 {\
638 ptr->ReleaseRef();\
639 }\
640 }\
641 void BindingsInitializer_dummy()
642 #endif
643
644 template<typename ClassType,
645 typename BaseClass = bindings_internal::NoBaseClass,
646 typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::typ e* = nullptr>
647 class class_
648 {
649 public:
650 class_(const char* name)
651 {
652 bindings_internal::register_class(name,
653 bindings_internal::TypeInfo<ClassType>(),
654 bindings_internal::TypeInfo<BaseClass>());
655 }
656
657 template<typename FieldType>
658 const class_& property(const char* name,
659 FieldType (ClassType::*getter)() const,
660 void (ClassType::*setter)(FieldType) = nullptr) const
661 {
662 bindings_internal::register_property(
663 bindings_internal::TypeInfo<ClassType>(), name, getter, setter);
664 return *this;
665 }
666
667 const class_& class_property(const char* name,
668 const char* jsValue) const
669 {
670 bindings_internal::register_property(
671 bindings_internal::TypeInfo<ClassType>(), name,
672 bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(),
673 jsValue);
674 return *this;
675 }
676
677 template<typename ReturnType, typename... Args>
678 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..)) const
679 {
680 bindings_internal::register_method(
681 bindings_internal::TypeInfo<ClassType>(), name, method);
682 return *this;
683 }
684
685 template<typename ReturnType, typename... Args>
686 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..) const) const
687 {
688 bindings_internal::register_method(
689 bindings_internal::TypeInfo<ClassType>(), name, method);
690 return *this;
691 }
692
693 template<typename ReturnType, typename... Args>
694 const class_& class_function(const char* name, ReturnType (*method)(Args...)) const
695 {
696 bindings_internal::register_method(
697 bindings_internal::TypeInfo<ClassType>(), name, method);
698 return *this;
699 }
700
701 const class_& class_initializer(void (*function)()) const
702 {
703 bindings_internal::register_initializer(
704 bindings_internal::TypeInfo<ClassType>(), function);
705 return *this;
706 }
707
708 template<typename ReturnType,
709 typename std::enable_if<std::is_convertible<ReturnType, int32_t>::value>:: type* = nullptr>
710 const class_& subclass_differentiator(ReturnType ClassType::* member,
711 std::initializer_list<std::pair<ReturnType, const char*>> list) const
712 {
713 ClassType* instance = nullptr;
714 size_t offset = (char*)&(instance->*member) - (char*)instance;
715
716 std::vector<std::pair<int, std::string>> mapping;
717 for (const auto& item : list)
718 mapping.emplace_back(item.first, item.second);
719
720 bindings_internal::register_differentiator(
721 bindings_internal::TypeInfo<ClassType>(), offset, mapping);
722 return *this;
723 }
724 };
OLDNEW
« no previous file with comments | « compiled/bindings.cpp ('k') | compiled/debug.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld