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

Delta Between Two Patch Sets: compiled/bindings.ipp

Issue 29333474: Issue 4125 - [emscripten] Convert filter classes to C++ (Closed)
Left Patch Set: Now passing all filter matching tests (without filter options) Created Jan. 18, 2016, 6:12 p.m.
Right Patch Set: Addressed comments from Patch Set 28 Created March 21, 2017, 10:04 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « compiled/bindings.cpp ('k') | compiled/debug.h » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(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 };
LEFTRIGHT

Powered by Google App Engine
This is Rietveld