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

Powered by Google App Engine
This is Rietveld