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

Side by Side Diff: compiled/bindings.h

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

Powered by Google App Engine
This is Rietveld