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

Side by Side Diff: compiled/bindings/generator.cpp

Issue 29410617: Issue 5131 - [emscripten] Clean separation of bindings code and runtime code (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore
Patch Set: Rebased Created April 20, 2017, 1:49 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
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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 #pragma once 18 #include "generator.h"
19 19
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstdio> 20 #include <cstdio>
23 #include <cstdlib> 21
24 #include <exception> 22 namespace
25 #include <functional> 23 {
26 #include <map> 24 std::vector<bindings_internal::ClassInfo> classes;
27 #include <string> 25 }
28 #include <type_traits>
29 #include <utility>
30 #include <vector>
31
32 #include <emscripten.h>
33
34 #include "String.h"
35 #include "intrusive_ptr.h"
36 26
37 namespace bindings_internal 27 namespace bindings_internal
38 { 28 {
39 typedef void* TYPEID; 29 FunctionInfo::FunctionInfo()
40 30 {
41 enum class TypeCategory 31 name[0] = '\0';
42 { 32 }
43 UNKNOWN, 33
44 VOID, 34 FunctionInfo::FunctionInfo(TypeCategory returnType, TYPEID pointerType,
45 INT, 35 std::initializer_list<TypeCategory> argTypes, bool instance_function,
46 INT64, 36 void* function)
47 FLOAT, 37 : returnType(returnType), pointerType(pointerType),
48 DOUBLE, 38 instance_function(instance_function)
49 DEPENDENT_STRING, 39 {
50 OWNED_STRING, 40 name[0] = '\0';
51 STRING_REF, 41
52 CLASS_PTR 42 // The function parameter is a pointer to the function pointer.
53 }; 43 // Emscripten's "function pointers" are actually integers indicating the
54 44 // position in the call table. 0 represents nullptr.
55 template<typename T> 45 if (!*reinterpret_cast<int*>(function))
56 struct TypeInfo 46 return;
57 { 47
58 /* 48 std::string signature;
59 * Since TypeInfo is a templated type, in practice the compiler will define 49
60 * a new type for each possible template parameter value. We use that fact 50 // Add return type to the signature. Similar logic in Emscripten:
61 * to generate type identifiers: each of these TypeInfo types has a 51 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L46
62 * different s_typeIDHelper member, so we use a pointer to that static 52 switch (returnType)
63 * variable as a type identifier - it will be different for each template
64 * parameter.
65 */
66 static char s_typeIDHelper;
67 constexpr operator TYPEID() const
68 { 53 {
69 return &s_typeIDHelper; 54 case TypeCategory::DEPENDENT_STRING:
55 case TypeCategory::OWNED_STRING:
56 // Technically, objects aren't really returned with clang. The caller
57 // instead adds the reference to the resulting object as an implicit
58 // parameter.
59 signature += "vi";
60 break;
61 case TypeCategory::VOID:
62 signature += 'v';
63 break;
64 case TypeCategory::FLOAT:
65 signature += 'f';
66 break;
67 case TypeCategory::DOUBLE:
68 signature += 'd';
69 break;
70 case TypeCategory::INT:
71 case TypeCategory::INT64:
72 case TypeCategory::STRING_REF:
73 case TypeCategory::CLASS_PTR:
74 signature += 'i';
75 break;
76 default:
77 throw std::runtime_error("Unexpected function return type");
70 } 78 }
71 79
72 constexpr operator TypeCategory() const 80 // `this` pointer is an implicit parameter with clang and should be added
81 // to the signature.
82 if (instance_function)
83 signature += 'i';
84
85 // Add explicit parameters to the signature, Similar logic in Emscripten:
86 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L67
87 for (const auto& type : argTypes)
73 { 88 {
74 if (std::is_void<T>()) 89 switch (type)
75 return TypeCategory::VOID; 90 {
76
77 if (std::is_same<T, uint64_t>())
78 return TypeCategory::INT64;
79
80 if (std::is_integral<T>() || std::is_enum<T>())
81 return TypeCategory::INT;
82
83 if (std::is_same<T, float>())
84 return TypeCategory::FLOAT;
85
86 if (std::is_same<T, double>())
87 return TypeCategory::DOUBLE;
88
89 if (std::is_same<DependentString, T>() || std::is_same<const DependentStri ng, T>())
90 return TypeCategory::DEPENDENT_STRING;
91
92 if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>() )
93 return TypeCategory::OWNED_STRING;
94
95 if (std::is_same<String&, T>() || std::is_same<const String&, T>() ||
96 std::is_same<DependentString&, T>())
97 {
98 return TypeCategory::STRING_REF;
99 }
100
101 if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>: :type>())
102 return TypeCategory::CLASS_PTR;
103
104 return TypeCategory::UNKNOWN;
105 }
106
107 constexpr TYPEID pointer_type() const
108 {
109 if (std::is_pointer<T>())
110 return TypeInfo<typename std::remove_pointer<T>::type>();
111 else
112 return nullptr;
113 }
114 };
115
116 template<typename T>
117 char TypeInfo<T>::s_typeIDHelper;
118
119 struct FunctionInfo
120 {
121 TypeCategory returnType;
122 TYPEID pointerType;
123 std::vector<TypeCategory> args;
124 bool instance_function;
125 int effectiveArgs;
126 TypeCategory effectiveReturnType;
127 char name[1024];
128
129 FunctionInfo()
130 {
131 name[0] = '\0';
132 }
133
134 FunctionInfo(TypeCategory returnType, TYPEID pointerType,
135 std::initializer_list<TypeCategory> argTypes, bool instance_function,
136 void* function)
137 : returnType(returnType), pointerType(pointerType),
138 instance_function(instance_function)
139 {
140 name[0] = '\0';
141
142 // The function parameter is a pointer to the function pointer.
143 // Emscripten's "function pointers" are actually integers indicating the
144 // position in the call table. 0 represents nullptr.
145 if (!*reinterpret_cast<int*>(function))
146 return;
147
148 std::string signature;
149
150 // Add return type to the signature. Similar logic in Emscripten:
151 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L46
152 switch (returnType)
153 {
154 case TypeCategory::DEPENDENT_STRING:
155 case TypeCategory::OWNED_STRING:
156 // Technically, objects aren't really returned with clang. The caller
157 // instead adds the reference to the resulting object as an implicit
158 // parameter.
159 signature += "vi";
160 break;
161 case TypeCategory::VOID:
162 signature += 'v';
163 break;
164 case TypeCategory::FLOAT:
165 signature += 'f';
166 break;
167 case TypeCategory::DOUBLE:
168 signature += 'd';
169 break;
170 case TypeCategory::INT: 91 case TypeCategory::INT:
171 case TypeCategory::INT64:
172 case TypeCategory::STRING_REF: 92 case TypeCategory::STRING_REF:
173 case TypeCategory::CLASS_PTR: 93 case TypeCategory::CLASS_PTR:
174 signature += 'i'; 94 signature += 'i';
175 break; 95 break;
96 case TypeCategory::INT64:
97 // See https://github.com/kripken/emscripten/blob/1.37.3/src/modules.j s#L73,
98 // numerical types larger than 32-bit are split into multiple
99 // 32-bit parameters.
100 signature += "ii";
101 break;
102 case TypeCategory::FLOAT:
103 signature += 'f';
104 break;
105 case TypeCategory::DOUBLE:
106 signature += 'd';
107 break;
176 default: 108 default:
177 throw std::runtime_error("Unexpected function return type"); 109 throw std::runtime_error("Unexpected function argument type");
178 } 110 }
179 111 args.push_back(type);
180 // `this` pointer is an implicit parameter with clang and should be added 112 }
181 // to the signature. 113
182 if (instance_function) 114 get_function_name(function, signature.c_str());
183 signature += 'i'; 115 }
184 116
185 // Add explicit parameters to the signature, Similar logic in Emscripten: 117 bool FunctionInfo::empty() const
186 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L67 118 {
187 for (const auto& type : argTypes) 119 return name[0] == '\0';
188 { 120 }
189 switch (type) 121
122 void FunctionInfo::get_function_name(void* ptr, const char* signature)
123 {
124 // This is a hack, C++ won't let us get the mangled function name.
125 // JavaScript is more dynamic so we pass the pointer to our function
126 // there. With that and the function signature we can call the function -
127 // with a full stack so that we will cause it to abort. Sometimes the
128 // function we are calling will also be missing from the build. The result
129 // is the same: abort() is called which in turn calls stackTrace(). By
130 // replacing stackTrace() we get access to the call stack and search it
131 // for the name of our function.
132
133 EM_ASM_ARGS({
134 var signature = AsciiToString($2);
135 var args = [];
136 for (var i = 1; i < signature.length; i++)
137 args.push(0);
138
139 var oldPrint = Module.print;
140 var oldPrintErr = Module.printErr;
141 var oldStackTrace = stackTrace;
142 var sp = Runtime.stackSave();
143 Module.print = function(){};
144 Module.printErr = function(){};
145 stackTrace = function()
146 {
147 var stack = [];
148 for (var f = arguments.callee.caller; f; f = f.caller)
190 { 149 {
191 case TypeCategory::INT: 150 if (f.name)
192 case TypeCategory::STRING_REF: 151 {
193 case TypeCategory::CLASS_PTR: 152 if (f.name.indexOf("dynCall") == 0)
194 signature += 'i'; 153 break;
195 break; 154 else
196 case TypeCategory::INT64: 155 stack.push(f.name);
197 // See https://github.com/kripken/emscripten/blob/1.37.3/src/modules .js#L73, 156 }
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");
210 } 157 }
211 args.push_back(type); 158
212 } 159 result = stack[stack.length - 1];
213 160 if (result && result.indexOf("__wrapper") >= 0)
214 get_function_name(function, signature.c_str()); 161 result = stack[stack.length - 2];
215 } 162 throw result;
216 163 };
217 template<typename ReturnType, typename... Args> 164
218 FunctionInfo(ReturnType (*function)(Args...)) 165 Runtime.stackRestore(STACK_MAX);
219 : FunctionInfo(TypeInfo<ReturnType>(), 166
220 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, 167 try
221 &function) 168 {
222 { 169 Runtime.dynCall(signature, HEAP32[$1 >> 2], args);
223 } 170 }
224 171 catch(e)
225 template<typename ClassType, typename ReturnType, typename... Args> 172 {
226 FunctionInfo(ReturnType (ClassType::*function)(Args...)) 173 Module.stringToAscii(e, $0);
227 : FunctionInfo(TypeInfo<ReturnType>(), 174 }
228 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, 175 finally
229 &function) 176 {
230 { 177 Runtime.stackRestore(sp);
231 } 178 Module.print = oldPrint;
232 179 Module.printErr = oldPrintErr;
233 template<typename ClassType, typename ReturnType, typename... Args> 180 stackTrace = oldStackTrace;
234 FunctionInfo(ReturnType (ClassType::*function)(Args...) const) 181 }
235 : FunctionInfo(TypeInfo<ReturnType>(), 182 }, name, ptr, signature);
236 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, 183 }
237 &function) 184
238 { 185 ClassInfo* find_class(TYPEID classID)
239 } 186 {
240 187 for (auto& classInfo : classes)
241 bool empty() const 188 if (classInfo.id == classID)
242 { 189 return &classInfo;
243 return name[0] == '\0'; 190 return nullptr;
244 } 191 }
245
246 void get_function_name(void* ptr, const char* signature)
247 {
248 // This is a hack, C++ won't let us get the mangled function name.
249 // JavaScript is more dynamic so we pass the pointer to our function
250 // there. With that and the function signature we can call the function -
251 // with a full stack so that we will cause it to abort. Sometimes the
252 // function we are calling will also be missing from the build. The result
253 // is the same: abort() is called which in turn calls stackTrace(). By
254 // replacing stackTrace() we get access to the call stack and search it
255 // for the name of our function.
256
257 EM_ASM_ARGS({
258 var signature = AsciiToString($2);
259 var args = [];
260 for (var i = 1; i < signature.length; i++)
261 args.push(0);
262
263 var oldPrint = Module.print;
264 var oldPrintErr = Module.printErr;
265 var oldStackTrace = stackTrace;
266 var sp = Runtime.stackSave();
267 Module.print = function(){};
268 Module.printErr = function(){};
269 stackTrace = function()
270 {
271 var stack = [];
272 for (var f = arguments.callee.caller; f; f = f.caller)
273 {
274 if (f.name)
275 {
276 if (f.name.indexOf("dynCall") == 0)
277 break;
278 else
279 stack.push(f.name);
280 }
281 }
282
283 result = stack[stack.length - 1];
284 if (result && result.indexOf("__wrapper") >= 0)
285 result = stack[stack.length - 2];
286 throw result;
287 };
288
289 Runtime.stackRestore(STACK_MAX);
290
291 try
292 {
293 Runtime.dynCall(signature, HEAP32[$1 >> 2], args);
294 }
295 catch(e)
296 {
297 Module.stringToAscii(e, $0);
298 }
299 finally
300 {
301 Runtime.stackRestore(sp);
302 Module.print = oldPrint;
303 Module.printErr = oldPrintErr;
304 stackTrace = oldStackTrace;
305 }
306 }, name, ptr, signature);
307 }
308 };
309
310 class NoBaseClass
311 {
312 };
313
314 struct PropertyInfo
315 {
316 std::string name;
317 FunctionInfo getter;
318 FunctionInfo setter;
319 std::string jsValue;
320 };
321
322 struct MethodInfo
323 {
324 std::string name;
325 FunctionInfo call;
326 };
327
328 struct DifferentiatorInfo
329 {
330 size_t offset;
331 std::vector<std::pair<int, std::string>> mapping;
332 };
333
334 struct ClassInfo
335 {
336 ClassInfo* baseClass;
337 std::string name;
338 std::vector<PropertyInfo> properties;
339 std::vector<MethodInfo> methods;
340 DifferentiatorInfo subclass_differentiator;
341 ptrdiff_t ref_counted_offset;
342 };
343
344 typedef std::function<void()> CustomGenerator;
345
346 std::map<TYPEID, ClassInfo> classes;
347 std::vector<CustomGenerator> customGenerators;
348 192
349 void register_class(const char* name, TYPEID classID, TYPEID baseClassID, 193 void register_class(const char* name, TYPEID classID, TYPEID baseClassID,
350 ptrdiff_t ref_counted_offset) 194 ptrdiff_t ref_counted_offset)
351 { 195 {
352 auto it = classes.find(classID); 196 if (find_class(classID))
353 if (it != classes.end())
354 throw std::runtime_error(std::string("Duplicate definition for class ") + name); 197 throw std::runtime_error(std::string("Duplicate definition for class ") + name);
355 198
356 ClassInfo* baseClass = nullptr; 199 if (baseClassID != TypeInfo<NoBaseClass>() && !find_class(baseClassID))
357 if (baseClassID != TypeInfo<NoBaseClass>()) 200 throw std::runtime_error(std::string("Unknown base class defined for class ") + name);
358 {
359 it = classes.find(baseClassID);
360 if (it == classes.end())
361 throw std::runtime_error(std::string("Unknown base class defined for cla ss ") + name);
362 baseClass = &(it->second);
363 }
364 201
365 ClassInfo classInfo; 202 ClassInfo classInfo;
366 classInfo.baseClass = baseClass; 203 classInfo.id = classID;
204 classInfo.baseClass = baseClassID;
367 classInfo.name = name; 205 classInfo.name = name;
368 classInfo.subclass_differentiator.offset = SIZE_MAX; 206 classInfo.subclass_differentiator.offset = SIZE_MAX;
369 classInfo.ref_counted_offset = ref_counted_offset; 207 classInfo.ref_counted_offset = ref_counted_offset;
370 classes[classID] = classInfo; 208 classes.push_back(classInfo);
371 } 209 }
372 210
373 void register_property(TYPEID classID, const char* name, 211 void register_property(TYPEID classID, const char* name,
374 const FunctionInfo& getter, const FunctionInfo& setter, 212 const FunctionInfo& getter, const FunctionInfo& setter,
375 const char* jsValue = "") 213 const char* jsValue)
376 { 214 {
377 auto it = classes.find(classID); 215 ClassInfo* classInfo = find_class(classID);
378 if (it == classes.end()) 216 if (!classInfo)
379 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name); 217 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name);
380 218
381 PropertyInfo propertyInfo; 219 PropertyInfo propertyInfo;
382 propertyInfo.name = name; 220 propertyInfo.name = name;
383 propertyInfo.getter = getter; 221 propertyInfo.getter = getter;
384 propertyInfo.setter = setter; 222 propertyInfo.setter = setter;
385 propertyInfo.jsValue = jsValue; 223 propertyInfo.jsValue = jsValue;
386 it->second.properties.push_back(propertyInfo); 224 classInfo->properties.push_back(propertyInfo);
387 } 225 }
388 226
389 void register_method(TYPEID classID, const char* name, 227 void register_method(TYPEID classID, const char* name,
390 const FunctionInfo& call) 228 const FunctionInfo& call)
391 { 229 {
392 auto it = classes.find(classID); 230 ClassInfo* classInfo = find_class(classID);
393 if (it == classes.end()) 231 if (!classInfo)
394 throw std::runtime_error(std::string("Method defined on unknown class: ") + name); 232 throw std::runtime_error(std::string("Method defined on unknown class: ") + name);
395 233
396 MethodInfo methodInfo; 234 MethodInfo methodInfo;
397 methodInfo.name = name; 235 methodInfo.name = name;
398 methodInfo.call = call; 236 methodInfo.call = call;
399 it->second.methods.push_back(methodInfo); 237 classInfo->methods.push_back(methodInfo);
400 } 238 }
401 239
402 void register_differentiator(TYPEID classID, size_t offset, 240 void register_differentiator(TYPEID classID, size_t offset,
403 std::vector<std::pair<int, std::string>>& mapping) 241 std::vector<std::pair<int, std::string>>& mapping)
404 { 242 {
405 auto it = classes.find(classID); 243 ClassInfo* classInfo = find_class(classID);
406 if (it == classes.end()) 244 if (!classInfo)
407 throw std::runtime_error("Subclass differentiator defined on unknown class "); 245 throw std::runtime_error("Subclass differentiator defined on unknown class ");
408 246
409 if (it->second.subclass_differentiator.offset != SIZE_MAX) 247 if (classInfo->subclass_differentiator.offset != SIZE_MAX)
410 throw std::runtime_error("More than one subclass differentiator defined fo r class " + it->second.name); 248 throw std::runtime_error("More than one subclass differentiator defined fo r class " + classInfo->name);
411 249
412 DifferentiatorInfo differentiatorInfo; 250 DifferentiatorInfo differentiatorInfo;
413 differentiatorInfo.offset = offset; 251 differentiatorInfo.offset = offset;
414 differentiatorInfo.mapping = mapping; 252 differentiatorInfo.mapping = mapping;
415 it->second.subclass_differentiator = differentiatorInfo; 253 classInfo->subclass_differentiator = differentiatorInfo;
416 } 254 }
417 255
418 const std::string generateCall(const FunctionInfo& call, 256 const std::string generateCall(const FunctionInfo& call,
419 std::vector<std::string>& params) 257 std::vector<std::string>& params)
420 { 258 {
421 if (call.returnType == TypeCategory::DEPENDENT_STRING || 259 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
422 call.returnType == TypeCategory::OWNED_STRING) 260 call.returnType == TypeCategory::OWNED_STRING)
423 { 261 {
424 params.insert(params.begin(), "string"); 262 params.insert(params.begin(), "string");
425 } 263 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 } 297 }
460 case TypeCategory::STRING_REF: 298 case TypeCategory::STRING_REF:
461 return " var result = readString(" + call_str + ");\n"; 299 return " var result = readString(" + call_str + ");\n";
462 case TypeCategory::CLASS_PTR: 300 case TypeCategory::CLASS_PTR:
463 { 301 {
464 std::string result; 302 std::string result;
465 result += " var result = " + call_str + ";\n"; 303 result += " var result = " + call_str + ";\n";
466 result += " if (result)\n"; 304 result += " if (result)\n";
467 result += " {\n"; 305 result += " {\n";
468 306
469 auto it = classes.find(call.pointerType); 307 const ClassInfo* cls = find_class(call.pointerType);
470 if (it == classes.end()) 308 if (!cls)
471 throw std::runtime_error("Function " + std::string(call.name) + " retu rns pointer to unknown class"); 309 throw std::runtime_error("Function " + std::string(call.name) + " retu rns pointer to unknown class");
472 310
473 const ClassInfo& cls = it->second; 311 auto offset = cls->subclass_differentiator.offset;
474 auto offset = cls.subclass_differentiator.offset;
475 if (offset == SIZE_MAX) 312 if (offset == SIZE_MAX)
476 result += " result = exports." + cls.name + "(result);\n"; 313 result += " result = exports." + cls->name + "(result);\n";
477 else 314 else
478 result += " result = exports." + cls.name + ".fromPointer(result);\ n"; 315 result += " result = exports." + cls->name + ".fromPointer(result); \n";
479 316
480 result += " }\n"; 317 result += " }\n";
481 result += " else\n"; 318 result += " else\n";
482 result += " result = null;\n"; 319 result += " result = null;\n";
483 return result; 320 return result;
484 } 321 }
485 default: 322 default:
486 throw std::runtime_error("Unexpected return type for " + std::string(cal l.name)); 323 throw std::runtime_error("Unexpected return type for " + std::string(cal l.name));
487 } 324 }
488 } 325 }
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
592 { 429 {
593 Module._ReleaseRef(this._pointer + ref_counted_offset); 430 Module._ReleaseRef(this._pointer + ref_counted_offset);
594 }; 431 };
595 return result; 432 return result;
596 } 433 }
597 )"); 434 )");
598 } 435 }
599 436
600 void printClass(const ClassInfo& cls) 437 void printClass(const ClassInfo& cls)
601 { 438 {
439 ClassInfo* baseClass = find_class(cls.baseClass);
602 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), 440 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(),
603 (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "null"), 441 (baseClass ? ("exports." + baseClass->name).c_str() : "null"),
604 cls.ref_counted_offset); 442 cls.ref_counted_offset);
605 443
606 DifferentiatorInfo differentiator = cls.subclass_differentiator; 444 DifferentiatorInfo differentiator = cls.subclass_differentiator;
607 if (differentiator.offset != SIZE_MAX) 445 if (differentiator.offset != SIZE_MAX)
608 { 446 {
609 printf("var %s_mapping = \n", cls.name.c_str()); 447 printf("var %s_mapping = \n", cls.name.c_str());
610 puts("{"); 448 puts("{");
611 for (const auto& item : differentiator.mapping) 449 for (const auto& item : differentiator.mapping)
612 printf(" %i: '%s',\n", item.first, item.second.c_str()); 450 printf(" %i: '%s',\n", item.first, item.second.c_str());
613 puts("};"); 451 puts("};");
(...skipping 23 matching lines...) Expand all
637 475
638 for (const auto& item : cls.methods) 476 for (const auto& item : cls.methods)
639 { 477 {
640 std::string obj("exports." + cls.name); 478 std::string obj("exports." + cls.name);
641 if (item.call.instance_function) 479 if (item.call.instance_function)
642 obj += ".prototype"; 480 obj += ".prototype";
643 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), 481 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(),
644 wrapCall(item.call).c_str()); 482 wrapCall(item.call).c_str());
645 } 483 }
646 } 484 }
647
648 void printBindings()
649 {
650 printHelpers();
651
652 for (const auto& item : classes)
653 printClass(item.second);
654
655 for (const auto& item : customGenerators)
656 item();
657 }
658 } 485 }
659 486
660 #if defined(PRINT_BINDINGS) 487 void printBindings()
661 // Bindings generation step: collect bindings information and print 488 {
662 // corresponding JS code. 489 bindings_internal::printHelpers();
663 #define EMSCRIPTEN_BINDINGS \
664 void InitializeBindings();\
665 int main()\
666 {\
667 try\
668 {\
669 InitializeBindings();\
670 bindings_internal::printBindings();\
671 }\
672 catch (const std::exception& e)\
673 {\
674 EM_ASM_ARGS(\
675 console.error("Error occurred generating JavaScript bindings: " +\
676 Module.AsciiToString($0)), e.what()\
677 );\
678 abort();\
679 }\
680 return 0;\
681 }\
682 void InitializeBindings()
683 #else
684 // Actual compilation step: ignore bindings information but define some
685 // exported helper functions necessary for the bindings.
686 #define EMSCRIPTEN_BINDINGS \
687 extern "C"\
688 {\
689 void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\
690 String::value_type* data, String::size_type len)\
691 {\
692 /* String is already allocated on stack, we merely need to call*/\
693 /* constructor.*/\
694 new (str) DependentString(data, len);\
695 }\
696 void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\
697 {\
698 /* Stack memory will be freed automatically, we need to call*/\
699 /* destructor explicitly however.*/\
700 str->~OwnedString();\
701 }\
702 String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\
703 const String& str)\
704 {\
705 return str.length();\
706 }\
707 const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\
708 const String& str)\
709 {\
710 return str.data();\
711 }\
712 void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\
713 {\
714 ptr->ReleaseRef();\
715 }\
716 }\
717 void BindingsInitializer_dummy()
718 #endif
719 490
720 template<typename ClassType, 491 for (const auto& item : classes)
721 typename BaseClass = bindings_internal::NoBaseClass, 492 bindings_internal::printClass(item);
722 typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::typ e* = nullptr>
723 class class_
724 {
725 public:
726 class_(const char* name)
727 {
728 ClassType* ptr = reinterpret_cast<ClassType*>(0x10000000);
729 ptrdiff_t ref_counted_offset =
730 reinterpret_cast<char*>(static_cast<ref_counted*>(ptr)) -
731 reinterpret_cast<char*>(ptr);
732 bindings_internal::register_class(name,
733 bindings_internal::TypeInfo<ClassType>(),
734 bindings_internal::TypeInfo<BaseClass>(),
735 ref_counted_offset
736 );
737 }
738
739 template<typename FieldType>
740 const class_& property(const char* name,
741 FieldType (ClassType::*getter)() const,
742 void (ClassType::*setter)(FieldType) = nullptr) const
743 {
744 bindings_internal::register_property(
745 bindings_internal::TypeInfo<ClassType>(), name, getter, setter);
746 return *this;
747 }
748
749 const class_& class_property(const char* name,
750 const char* jsValue) const
751 {
752 bindings_internal::register_property(
753 bindings_internal::TypeInfo<ClassType>(), name,
754 bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(),
755 jsValue);
756 return *this;
757 }
758
759 template<typename ReturnType, typename... Args>
760 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..)) const
761 {
762 bindings_internal::register_method(
763 bindings_internal::TypeInfo<ClassType>(), name, method);
764 return *this;
765 }
766
767 template<typename ReturnType, typename... Args>
768 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..) const) const
769 {
770 bindings_internal::register_method(
771 bindings_internal::TypeInfo<ClassType>(), name, method);
772 return *this;
773 }
774
775 template<typename ReturnType, typename... Args>
776 const class_& class_function(const char* name, ReturnType (*method)(Args...)) const
777 {
778 bindings_internal::register_method(
779 bindings_internal::TypeInfo<ClassType>(), name, method);
780 return *this;
781 }
782
783 template<typename ReturnType,
784 typename std::enable_if<std::is_convertible<ReturnType, int32_t>::value>:: type* = nullptr>
785 const class_& subclass_differentiator(ReturnType ClassType::* member,
786 std::initializer_list<std::pair<ReturnType, const char*>> list) const
787 {
788 ClassType* instance = nullptr;
789 size_t offset = (char*)&(instance->*member) - (char*)instance;
790
791 std::vector<std::pair<int, std::string>> mapping;
792 for (const auto& item : list)
793 mapping.emplace_back(item.first, item.second);
794
795 bindings_internal::register_differentiator(
796 bindings_internal::TypeInfo<ClassType>(), offset, mapping);
797 return *this;
798 }
799 };
800
801 void custom_generator(bindings_internal::CustomGenerator generator)
802 {
803 bindings_internal::customGenerators.push_back(generator);
804 } 493 }
OLDNEW

Powered by Google App Engine
This is Rietveld