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

Powered by Google App Engine
This is Rietveld