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

Delta Between Two Patch Sets: compiled/bindings.ipp

Issue 29398655: Issue 5062 - [emscripten] Allow generation of custom bindings code (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore
Left Patch Set: Created March 30, 2017, 7:59 a.m.
Right Patch Set: Rebased Created April 13, 2017, 1:06 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « compiled/bindings.cpp ('k') | compiled/filter/RegExpFilter.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
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 25 matching lines...) Expand all
36 36
37 namespace bindings_internal 37 namespace bindings_internal
38 { 38 {
39 typedef void* TYPEID; 39 typedef void* TYPEID;
40 40
41 enum class TypeCategory 41 enum class TypeCategory
42 { 42 {
43 UNKNOWN, 43 UNKNOWN,
44 VOID, 44 VOID,
45 INT, 45 INT,
46 INT64,
46 FLOAT, 47 FLOAT,
48 DOUBLE,
47 DEPENDENT_STRING, 49 DEPENDENT_STRING,
48 OWNED_STRING, 50 OWNED_STRING,
49 STRING_REF, 51 STRING_REF,
50 CLASS_PTR 52 CLASS_PTR
51 }; 53 };
52 54
53 template<typename T> 55 template<typename T>
54 struct TypeInfo 56 struct TypeInfo
55 { 57 {
56 /* 58 /*
57 * Since TypeInfo is a templated type, in practice the compiler will define 59 * Since TypeInfo is a templated type, in practice the compiler will define
58 * a new type for each possible template parameter value. We use that fact 60 * a new type for each possible template parameter value. We use that fact
59 * to generate type identifiers: each of these TypeInfo types has a 61 * to generate type identifiers: each of these TypeInfo types has a
60 * different s_typeIDHelper member, so we use a pointer to that static 62 * different s_typeIDHelper member, so we use a pointer to that static
61 * variable as a type identifier - it will be different for each template 63 * variable as a type identifier - it will be different for each template
62 * parameter. 64 * parameter.
63 */ 65 */
64 static char s_typeIDHelper; 66 static char s_typeIDHelper;
65 constexpr operator TYPEID() const 67 constexpr operator TYPEID() const
66 { 68 {
67 return &s_typeIDHelper; 69 return &s_typeIDHelper;
68 } 70 }
69 71
70 constexpr operator TypeCategory() const 72 constexpr operator TypeCategory() const
71 { 73 {
72 if (std::is_void<T>()) 74 if (std::is_void<T>())
73 return TypeCategory::VOID; 75 return TypeCategory::VOID;
74 76
77 if (std::is_same<T, uint64_t>())
78 return TypeCategory::INT64;
79
75 if (std::is_integral<T>() || std::is_enum<T>()) 80 if (std::is_integral<T>() || std::is_enum<T>())
76 return TypeCategory::INT; 81 return TypeCategory::INT;
77 82
78 if (std::is_floating_point<T>()) 83 if (std::is_same<T, float>())
79 return TypeCategory::FLOAT; 84 return TypeCategory::FLOAT;
85
86 if (std::is_same<T, double>())
87 return TypeCategory::DOUBLE;
80 88
81 if (std::is_same<DependentString, T>() || std::is_same<const DependentStri ng, T>()) 89 if (std::is_same<DependentString, T>() || std::is_same<const DependentStri ng, T>())
82 return TypeCategory::DEPENDENT_STRING; 90 return TypeCategory::DEPENDENT_STRING;
83 91
84 if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>() ) 92 if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>() )
85 return TypeCategory::OWNED_STRING; 93 return TypeCategory::OWNED_STRING;
86 94
87 if (std::is_same<String&, T>() || std::is_same<const String&, T>() || 95 if (std::is_same<String&, T>() || std::is_same<const String&, T>() ||
88 std::is_same<DependentString&, T>()) 96 std::is_same<DependentString&, T>())
89 { 97 {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 instance_function(instance_function) 138 instance_function(instance_function)
131 { 139 {
132 name[0] = '\0'; 140 name[0] = '\0';
133 141
134 // The function parameter is a pointer to the function pointer. 142 // The function parameter is a pointer to the function pointer.
135 // Emscripten's "function pointers" are actually integers indicating the 143 // Emscripten's "function pointers" are actually integers indicating the
136 // position in the call table. 0 represents nullptr. 144 // position in the call table. 0 represents nullptr.
137 if (!*reinterpret_cast<int*>(function)) 145 if (!*reinterpret_cast<int*>(function))
138 return; 146 return;
139 147
140 char signature[256]; 148 std::string signature;
141 int pos = 0; 149
142 if (returnType == TypeCategory::DEPENDENT_STRING || 150 // Add return type to the signature. Similar logic in Emscripten:
143 returnType == TypeCategory::OWNED_STRING) 151 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L46
144 { 152 switch (returnType)
145 // Objects aren't really returned but passed as parameter. Note that 153 {
146 // this pointer might come before it but we don't care because both 154 case TypeCategory::DEPENDENT_STRING:
147 // are integers (pointers) as far as Emscripten is concerned. 155 case TypeCategory::OWNED_STRING:
148 signature[pos++] = 'v'; 156 // Technically, objects aren't really returned with clang. The caller
149 signature[pos++] = 'i'; 157 // instead adds the reference to the resulting object as an implicit
150 } 158 // parameter.
151 else if (returnType == TypeCategory::VOID) 159 signature += "vi";
152 signature[pos++] = 'v'; 160 break;
153 else if (returnType == TypeCategory::FLOAT) 161 case TypeCategory::VOID:
154 signature[pos++] = 'd'; 162 signature += 'v';
155 else if (returnType == TypeCategory::INT || 163 break;
156 returnType == TypeCategory::STRING_REF || 164 case TypeCategory::FLOAT:
157 returnType == TypeCategory::CLASS_PTR) 165 signature += 'f';
158 { 166 break;
159 signature[pos++] = 'i'; 167 case TypeCategory::DOUBLE:
160 } 168 signature += 'd';
161 else 169 break;
162 throw std::runtime_error("Unexpected function return type"); 170 case TypeCategory::INT:
163 171 case TypeCategory::INT64:
172 case TypeCategory::STRING_REF:
173 case TypeCategory::CLASS_PTR:
174 signature += 'i';
175 break;
176 default:
177 throw std::runtime_error("Unexpected function return type");
178 }
179
180 // `this` pointer is an implicit parameter with clang and should be added
181 // to the signature.
164 if (instance_function) 182 if (instance_function)
165 { 183 signature += 'i';
166 // this pointer is an implicit parameter 184
167 signature[pos++] = 'i'; 185 // Add explicit parameters to the signature, Similar logic in Emscripten:
168 } 186 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L67
169 187 for (const auto& type : argTypes)
170 for (const auto& item : argTypes) 188 {
171 { 189 switch (type)
172 if (item == TypeCategory::INT || item == TypeCategory::STRING_REF || 190 {
173 item == TypeCategory::CLASS_PTR) 191 case TypeCategory::INT:
174 { 192 case TypeCategory::STRING_REF:
175 signature[pos++] = 'i'; 193 case TypeCategory::CLASS_PTR:
194 signature += 'i';
195 break;
196 case TypeCategory::INT64:
197 // See https://github.com/kripken/emscripten/blob/1.37.3/src/modules .js#L73,
198 // numerical types larger than 32-bit are split into multiple
199 // 32-bit parameters.
200 signature += "ii";
201 break;
202 case TypeCategory::FLOAT:
203 signature += 'f';
204 break;
205 case TypeCategory::DOUBLE:
206 signature += 'd';
207 break;
208 default:
209 throw std::runtime_error("Unexpected function argument type");
176 } 210 }
177 else if (item == TypeCategory::FLOAT) 211 args.push_back(type);
178 signature[pos++] = 'd'; 212 }
179 else 213
180 throw std::runtime_error("Unexpected function argument type"); 214 get_function_name(function, signature.c_str());
181 args.push_back(item);
182 }
183
184 signature[pos] = 0;
185
186 get_function_name(function, signature);
187 } 215 }
188 216
189 template<typename ReturnType, typename... Args> 217 template<typename ReturnType, typename... Args>
190 FunctionInfo(ReturnType (*function)(Args...)) 218 FunctionInfo(ReturnType (*function)(Args...))
191 : FunctionInfo(TypeInfo<ReturnType>(), 219 : FunctionInfo(TypeInfo<ReturnType>(),
192 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, 220 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false,
193 &function) 221 &function)
194 { 222 {
195 } 223 }
196 224
(...skipping 11 matching lines...) Expand all
208 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, 236 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true,
209 &function) 237 &function)
210 { 238 {
211 } 239 }
212 240
213 bool empty() const 241 bool empty() const
214 { 242 {
215 return name[0] == '\0'; 243 return name[0] == '\0';
216 } 244 }
217 245
218 void get_function_name(void* ptr, char* signature) 246 void get_function_name(void* ptr, const char* signature)
219 { 247 {
220 // This is a hack, C++ won't let us get the mangled function name. 248 // This is a hack, C++ won't let us get the mangled function name.
221 // JavaScript is more dynamic so we pass the pointer to our function 249 // JavaScript is more dynamic so we pass the pointer to our function
222 // there. With that and the function signature we can call the function - 250 // there. With that and the function signature we can call the function -
223 // with a full stack so that we will cause it to abort. Sometimes the 251 // with a full stack so that we will cause it to abort. Sometimes the
224 // function we are calling will also be missing from the build. The result 252 // function we are calling will also be missing from the build. The result
225 // is the same: abort() is called which in turn calls stackTrace(). By 253 // is the same: abort() is called which in turn calls stackTrace(). By
226 // replacing stackTrace() we get access to the call stack and search it 254 // replacing stackTrace() we get access to the call stack and search it
227 // for the name of our function. 255 // for the name of our function.
228 256
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 std::string call_str(call.name); 427 std::string call_str(call.name);
400 call_str += "("; 428 call_str += "(";
401 for (int i = 0; i < params.size(); i++) 429 for (int i = 0; i < params.size(); i++)
402 { 430 {
403 if (i > 0) 431 if (i > 0)
404 call_str += ", "; 432 call_str += ", ";
405 call_str += params[i]; 433 call_str += params[i];
406 } 434 }
407 call_str += ")"; 435 call_str += ")";
408 436
409 if (call.returnType == TypeCategory::VOID) 437 switch (call.returnType)
410 return " " + call_str + ";\n"; 438 {
411 else if (call.returnType == TypeCategory::INT || 439 case TypeCategory::VOID:
412 call.returnType == TypeCategory::FLOAT) 440 return " " + call_str + ";\n";
413 { 441 case TypeCategory::INT:
414 return " var result = " + call_str + ";\n"; 442 case TypeCategory::FLOAT:
415 } 443 case TypeCategory::DOUBLE:
416 else if (call.returnType == TypeCategory::DEPENDENT_STRING || 444 return " var result = " + call_str + ";\n";
417 call.returnType == TypeCategory::OWNED_STRING) 445 case TypeCategory::INT64:
418 { 446 return " var result = Runtime.makeBigInt(" + call_str + ", " +
419 std::string result; 447 "Runtime.getTempRet0(), " +
420 result += " var string = createString();\n"; 448 "true);\n";
421 result += " " + call_str + ";\n"; 449 case TypeCategory::DEPENDENT_STRING:
422 result += " var result = readString(string);\n"; 450 case TypeCategory::OWNED_STRING:
423 if (call.returnType == TypeCategory::OWNED_STRING) 451 {
424 result += " Module._DestroyString(string);\n"; 452 std::string result;
425 return result; 453 result += " var string = createString();\n";
426 } 454 result += " " + call_str + ";\n";
427 else if (call.returnType == TypeCategory::STRING_REF) 455 result += " var result = readString(string);\n";
428 { 456 if (call.returnType == TypeCategory::OWNED_STRING)
429 return " var result = readString(" + call_str + ");\n"; 457 result += " Module._DestroyString(string);\n";
430 } 458 return result;
431 else if (call.returnType == TypeCategory::CLASS_PTR) 459 }
432 { 460 case TypeCategory::STRING_REF:
433 std::string result; 461 return " var result = readString(" + call_str + ");\n";
434 result += " var result = " + call_str + ";\n"; 462 case TypeCategory::CLASS_PTR:
435 result += " if (result)\n"; 463 {
436 result += " {\n"; 464 std::string result;
437 465 result += " var result = " + call_str + ";\n";
438 auto it = classes.find(call.pointerType); 466 result += " if (result)\n";
439 if (it == classes.end()) 467 result += " {\n";
440 throw std::runtime_error("Function " + std::string(call.name) + " return s pointer to unknown class"); 468
441 469 auto it = classes.find(call.pointerType);
442 const ClassInfo& cls = it->second; 470 if (it == classes.end())
443 auto offset = cls.subclass_differentiator.offset; 471 throw std::runtime_error("Function " + std::string(call.name) + " retu rns pointer to unknown class");
444 if (offset == SIZE_MAX) 472
445 result += " result = " + cls.name + "(result);\n"; 473 const ClassInfo& cls = it->second;
446 else 474 auto offset = cls.subclass_differentiator.offset;
447 { 475 if (offset == SIZE_MAX)
448 result += " var type = HEAP32[result + " + std::to_string(offset)+ " >> 2];\n"; 476 result += " result = " + cls.name + "(result);\n";
449 result += " if (type in " + cls.name + "_mapping)\n"; 477 else
450 result += " result = new (exports[" + cls.name + "_mapping[type]])( result);\n"; 478 {
451 result += " else\n"; 479 result += " var type = HEAP32[result + " + std::to_string(offset)+ " >> 2];\n";
452 result += " throw new Error('Unexpected " + cls.name + " type: ' + type);\n"; 480 result += " if (type in " + cls.name + "_mapping)\n";
453 } 481 result += " result = new (exports[" + cls.name + "_mapping[type]] )(result);\n";
454 482 result += " else\n";
455 result += " }\n"; 483 result += " throw new Error('Unexpected " + cls.name + " type: ' + type);\n";
456 result += " else\n"; 484 }
457 result += " result = null;\n"; 485
458 return result; 486 result += " }\n";
459 } 487 result += " else\n";
460 else 488 result += " result = null;\n";
461 throw std::runtime_error("Unexpected return type for " + std::string(call. name)); 489 return result;
490 }
491 default:
492 throw std::runtime_error("Unexpected return type for " + std::string(cal l.name));
493 }
462 } 494 }
463 495
464 const std::string wrapCall(const FunctionInfo& call) 496 const std::string wrapCall(const FunctionInfo& call)
465 { 497 {
466 char buffer[20];
467 bool hasStringArgs = false; 498 bool hasStringArgs = false;
468 std::vector<std::string> params; 499 std::vector<std::string> params;
469 std::string prefix = "function("; 500 std::string prefix = "function(";
470 for (int i = 0; i < call.args.size(); i++) 501 for (int i = 0; i < call.args.size(); i++)
471 { 502 {
472 sprintf(buffer, "arg%i", i); 503 std::string argName("arg" + std::to_string(i));
473 if (i > 0) 504 if (i > 0)
474 prefix += ", "; 505 prefix += ", ";
475 prefix += buffer; 506 prefix += argName;
476 507
477 if (call.args[i] == TypeCategory::STRING_REF) 508 if (call.args[i] == TypeCategory::STRING_REF)
478 { 509 {
479 hasStringArgs = true; 510 hasStringArgs = true;
480 params.push_back(std::string("createString(") + buffer + ")"); 511 params.push_back(std::string("createString(") + argName + ")");
481 } 512 }
482 else if (call.args[i] == TypeCategory::CLASS_PTR) 513 else if (call.args[i] == TypeCategory::CLASS_PTR)
483 params.push_back(std::string(buffer) + "._pointer"); 514 params.push_back(argName + "._pointer");
515 else if (call.args[i] == TypeCategory::INT64)
516 {
517 // 64-bit integers are passed as two integer parameters
518 params.push_back(argName + " >>> 0");
519 params.push_back(argName + " / 0x100000000 >>> 0");
520 }
484 else 521 else
485 params.push_back(buffer); 522 params.push_back(argName);
486 } 523 }
487 prefix += ")\n{\n"; 524 prefix += ")\n{\n";
488 525
489 std::string suffix = "}"; 526 std::string suffix = "}";
490 if (call.returnType != TypeCategory::VOID) 527 if (call.returnType != TypeCategory::VOID)
491 suffix = " return result;\n" + suffix; 528 suffix = " return result;\n" + suffix;
492 529
493 if (call.returnType == TypeCategory::DEPENDENT_STRING || 530 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
494 call.returnType == TypeCategory::OWNED_STRING || hasStringArgs) 531 call.returnType == TypeCategory::OWNED_STRING || hasStringArgs)
495 { 532 {
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
608 645
609 for (const auto& item : customGenerators) 646 for (const auto& item : customGenerators)
610 item(); 647 item();
611 } 648 }
612 } 649 }
613 650
614 #if defined(PRINT_BINDINGS) 651 #if defined(PRINT_BINDINGS)
615 // Bindings generation step: collect bindings information and print 652 // Bindings generation step: collect bindings information and print
616 // corresponding JS code. 653 // corresponding JS code.
617 #define EMSCRIPTEN_BINDINGS \ 654 #define EMSCRIPTEN_BINDINGS \
618 struct BindingsInitializer {\ 655 void InitializeBindings();\
619 BindingsInitializer();\ 656 int main()\
620 };\
sergei 2017/03/30 11:09:17 I would make it a simple function `void Initialize
Wladimir Palant 2017/03/30 12:58:58 Done.
621 int main(void)\
sergei 2017/03/30 11:09:17 In C++ there is no need for specifying of void in
sergei 2017/03/30 11:09:17 Do you mind to consider in future moving of such C
Wladimir Palant 2017/03/30 12:58:58 Done.
Wladimir Palant 2017/03/30 12:58:58 Not sure what you mean. This code is inside bindin
sergei 2017/04/04 14:49:30 I mean it would be better to have this code in ano
Wladimir Palant 2017/04/04 15:41:48 But we are not compiling an executable, it's not s
sergei 2017/04/11 16:29:20 I didn't mean to append a cpp with main to SOURCE_
Wladimir Palant 2017/04/11 18:22:44 Let's do this in a follow-up, I'll file an issue.
622 {\ 657 {\
623 try\ 658 try\
624 {\ 659 {\
625 struct BindingsInitializer instance;\ 660 InitializeBindings();\
sergei 2017/03/30 11:09:17 Why did you need to add struct here?
Wladimir Palant 2017/03/30 12:58:57 This is a InitializeBindings() call now - this was
626 bindings_internal::printBindings();\ 661 bindings_internal::printBindings();\
627 }\ 662 }\
628 catch (const std::exception& e)\ 663 catch (const std::exception& e)\
629 {\ 664 {\
630 EM_ASM_ARGS(\ 665 EM_ASM_ARGS(\
631 console.error("Error occurred generating JavaScript bindings: " +\ 666 console.error("Error occurred generating JavaScript bindings: " +\
632 Module.AsciiToString($0)), e.what()\ 667 Module.AsciiToString($0)), e.what()\
633 );\ 668 );\
634 abort();\ 669 abort();\
635 }\ 670 }\
636 return 0;\ 671 return 0;\
637 }\ 672 }\
638 BindingsInitializer::BindingsInitializer() 673 void InitializeBindings()
639 #else 674 #else
640 // Actual compilation step: ignore bindings information but define some 675 // Actual compilation step: ignore bindings information but define some
641 // exported helper functions necessary for the bindings. 676 // exported helper functions necessary for the bindings.
642 #define EMSCRIPTEN_BINDINGS \ 677 #define EMSCRIPTEN_BINDINGS \
643 extern "C"\ 678 extern "C"\
644 {\ 679 {\
645 void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ 680 void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\
646 String::value_type* data, String::size_type len)\ 681 String::value_type* data, String::size_type len)\
647 {\ 682 {\
648 /* String is already allocated on stack, we merely need to call*/\ 683 /* String is already allocated on stack, we merely need to call*/\
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 bindings_internal::register_differentiator( 786 bindings_internal::register_differentiator(
752 bindings_internal::TypeInfo<ClassType>(), offset, mapping); 787 bindings_internal::TypeInfo<ClassType>(), offset, mapping);
753 return *this; 788 return *this;
754 } 789 }
755 }; 790 };
756 791
757 void custom_generator(bindings_internal::CustomGenerator generator) 792 void custom_generator(bindings_internal::CustomGenerator generator)
758 { 793 {
759 bindings_internal::customGenerators.push_back(generator); 794 bindings_internal::customGenerators.push_back(generator);
760 } 795 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld