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 13, 2017, 1:08 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/bindings/generator.h ('k') | compiled/bindings/main.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 std::vector<bindings_internal::CustomGenerator> customGenerators;
28 #include <type_traits> 26 }
29 #include <utility>
30 #include <vector>
31
32 #include <emscripten.h>
33
34 #include "String.h"
35 #include "intrusive_ptr.h"
36 27
37 namespace bindings_internal 28 namespace bindings_internal
38 { 29 {
39 typedef void* TYPEID; 30 FunctionInfo::FunctionInfo()
40 31 {
41 enum class TypeCategory 32 name[0] = '\0';
42 { 33 }
43 UNKNOWN, 34
44 VOID, 35 FunctionInfo::FunctionInfo(TypeCategory returnType, TYPEID pointerType,
45 INT, 36 std::initializer_list<TypeCategory> argTypes, bool instance_function,
46 INT64, 37 void* function)
47 FLOAT, 38 : returnType(returnType), pointerType(pointerType),
48 DOUBLE, 39 instance_function(instance_function)
49 DEPENDENT_STRING, 40 {
50 OWNED_STRING, 41 name[0] = '\0';
51 STRING_REF, 42
52 CLASS_PTR 43 // The function parameter is a pointer to the function pointer.
53 }; 44 // Emscripten's "function pointers" are actually integers indicating the
54 45 // position in the call table. 0 represents nullptr.
55 template<typename T> 46 if (!*reinterpret_cast<int*>(function))
56 struct TypeInfo 47 return;
57 { 48
58 /* 49 std::string signature;
59 * Since TypeInfo is a templated type, in practice the compiler will define 50
60 * a new type for each possible template parameter value. We use that fact 51 // Add return type to the signature. Similar logic in Emscripten:
61 * to generate type identifiers: each of these TypeInfo types has a 52 // 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 53 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 { 54 {
69 return &s_typeIDHelper; 55 case TypeCategory::DEPENDENT_STRING:
56 case TypeCategory::OWNED_STRING:
57 // Technically, objects aren't really returned with clang. The caller
58 // instead adds the reference to the resulting object as an implicit
59 // parameter.
60 signature += "vi";
61 break;
62 case TypeCategory::VOID:
63 signature += 'v';
64 break;
65 case TypeCategory::FLOAT:
66 signature += 'f';
67 break;
68 case TypeCategory::DOUBLE:
69 signature += 'd';
70 break;
71 case TypeCategory::INT:
72 case TypeCategory::INT64:
73 case TypeCategory::STRING_REF:
74 case TypeCategory::CLASS_PTR:
75 signature += 'i';
76 break;
77 default:
78 throw std::runtime_error("Unexpected function return type");
70 } 79 }
71 80
72 constexpr operator TypeCategory() const 81 // `this` pointer is an implicit parameter with clang and should be added
82 // to the signature.
83 if (instance_function)
84 signature += 'i';
85
86 // Add explicit parameters to the signature, Similar logic in Emscripten:
87 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L67
88 for (const auto& type : argTypes)
73 { 89 {
74 if (std::is_void<T>()) 90 switch (type)
75 return TypeCategory::VOID; 91 {
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: 92 case TypeCategory::INT:
171 case TypeCategory::INT64:
172 case TypeCategory::STRING_REF: 93 case TypeCategory::STRING_REF:
173 case TypeCategory::CLASS_PTR: 94 case TypeCategory::CLASS_PTR:
174 signature += 'i'; 95 signature += 'i';
175 break; 96 break;
97 case TypeCategory::INT64:
98 // See https://github.com/kripken/emscripten/blob/1.37.3/src/modules.j s#L73,
99 // numerical types larger than 32-bit are split into multiple
100 // 32-bit parameters.
101 signature += "ii";
102 break;
103 case TypeCategory::FLOAT:
104 signature += 'f';
105 break;
106 case TypeCategory::DOUBLE:
107 signature += 'd';
108 break;
176 default: 109 default:
177 throw std::runtime_error("Unexpected function return type"); 110 throw std::runtime_error("Unexpected function argument type");
178 } 111 }
179 112 args.push_back(type);
180 // `this` pointer is an implicit parameter with clang and should be added 113 }
181 // to the signature. 114
182 if (instance_function) 115 get_function_name(function, signature.c_str());
183 signature += 'i'; 116 }
184 117
185 // Add explicit parameters to the signature, Similar logic in Emscripten: 118 bool FunctionInfo::empty() const
186 // https://github.com/kripken/emscripten/blob/1.37.3/src/modules.js#L67 119 {
187 for (const auto& type : argTypes) 120 return name[0] == '\0';
188 { 121 }
189 switch (type) 122
123 void FunctionInfo::get_function_name(void* ptr, const char* signature)
124 {
125 // This is a hack, C++ won't let us get the mangled function name.
126 // JavaScript is more dynamic so we pass the pointer to our function
127 // there. With that and the function signature we can call the function -
128 // with a full stack so that we will cause it to abort. Sometimes the
129 // function we are calling will also be missing from the build. The result
130 // is the same: abort() is called which in turn calls stackTrace(). By
131 // replacing stackTrace() we get access to the call stack and search it
132 // for the name of our function.
133
134 EM_ASM_ARGS({
135 var signature = AsciiToString($2);
136 var args = [];
137 for (var i = 1; i < signature.length; i++)
138 args.push(0);
139
140 var oldPrint = Module.print;
141 var oldPrintErr = Module.printErr;
142 var oldStackTrace = stackTrace;
143 var sp = Runtime.stackSave();
144 Module.print = function(){};
145 Module.printErr = function(){};
146 stackTrace = function()
147 {
148 var stack = [];
149 for (var f = arguments.callee.caller; f; f = f.caller)
190 { 150 {
191 case TypeCategory::INT: 151 if (f.name)
192 case TypeCategory::STRING_REF: 152 {
193 case TypeCategory::CLASS_PTR: 153 if (f.name.indexOf("dynCall") == 0)
194 signature += 'i'; 154 break;
195 break; 155 else
196 case TypeCategory::INT64: 156 stack.push(f.name);
197 // See https://github.com/kripken/emscripten/blob/1.37.3/src/modules .js#L73, 157 }
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 } 158 }
211 args.push_back(type); 159
212 } 160 result = stack[stack.length - 1];
213 161 if (result && result.indexOf("__wrapper") >= 0)
214 get_function_name(function, signature.c_str()); 162 result = stack[stack.length - 2];
215 } 163 throw result;
216 164 };
217 template<typename ReturnType, typename... Args> 165
218 FunctionInfo(ReturnType (*function)(Args...)) 166 Runtime.stackRestore(STACK_MAX);
219 : FunctionInfo(TypeInfo<ReturnType>(), 167
220 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, 168 try
221 &function) 169 {
222 { 170 Runtime.dynCall(signature, HEAP32[$1 >> 2], args);
223 } 171 }
224 172 catch(e)
225 template<typename ClassType, typename ReturnType, typename... Args> 173 {
226 FunctionInfo(ReturnType (ClassType::*function)(Args...)) 174 Module.stringToAscii(e, $0);
227 : FunctionInfo(TypeInfo<ReturnType>(), 175 }
228 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, 176 finally
229 &function) 177 {
230 { 178 Runtime.stackRestore(sp);
231 } 179 Module.print = oldPrint;
232 180 Module.printErr = oldPrintErr;
233 template<typename ClassType, typename ReturnType, typename... Args> 181 stackTrace = oldStackTrace;
234 FunctionInfo(ReturnType (ClassType::*function)(Args...) const) 182 }
235 : FunctionInfo(TypeInfo<ReturnType>(), 183 }, name, ptr, signature);
236 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, 184 }
237 &function) 185
238 { 186 ClassInfo* find_class(TYPEID classID)
239 } 187 {
240 188 for (auto& classInfo : classes)
241 bool empty() const 189 if (classInfo.id == classID)
242 { 190 return &classInfo;
243 return name[0] == '\0'; 191 return nullptr;
244 } 192 }
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 193
349 void register_class(const char* name, TYPEID classID, TYPEID baseClassID, 194 void register_class(const char* name, TYPEID classID, TYPEID baseClassID,
350 ptrdiff_t ref_counted_offset) 195 ptrdiff_t ref_counted_offset)
351 { 196 {
352 auto it = classes.find(classID); 197 if (find_class(classID))
353 if (it != classes.end())
354 throw std::runtime_error(std::string("Duplicate definition for class ") + name); 198 throw std::runtime_error(std::string("Duplicate definition for class ") + name);
355 199
356 ClassInfo* baseClass = nullptr; 200 if (baseClassID != TypeInfo<NoBaseClass>() && !find_class(baseClassID))
357 if (baseClassID != TypeInfo<NoBaseClass>()) 201 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 202
365 ClassInfo classInfo; 203 ClassInfo classInfo;
366 classInfo.baseClass = baseClass; 204 classInfo.id = classID;
205 classInfo.baseClass = baseClassID;
367 classInfo.name = name; 206 classInfo.name = name;
368 classInfo.subclass_differentiator.offset = SIZE_MAX; 207 classInfo.subclass_differentiator.offset = SIZE_MAX;
369 classInfo.ref_counted_offset = ref_counted_offset; 208 classInfo.ref_counted_offset = ref_counted_offset;
370 classes[classID] = classInfo; 209 classes.push_back(classInfo);
371 } 210 }
372 211
373 void register_property(TYPEID classID, const char* name, 212 void register_property(TYPEID classID, const char* name,
374 const FunctionInfo& getter, const FunctionInfo& setter, 213 const FunctionInfo& getter, const FunctionInfo& setter,
375 const char* jsValue = "") 214 const char* jsValue)
376 { 215 {
377 auto it = classes.find(classID); 216 ClassInfo* classInfo = find_class(classID);
378 if (it == classes.end()) 217 if (!classInfo)
379 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name); 218 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name);
380 219
381 PropertyInfo propertyInfo; 220 PropertyInfo propertyInfo;
382 propertyInfo.name = name; 221 propertyInfo.name = name;
383 propertyInfo.getter = getter; 222 propertyInfo.getter = getter;
384 propertyInfo.setter = setter; 223 propertyInfo.setter = setter;
385 propertyInfo.jsValue = jsValue; 224 propertyInfo.jsValue = jsValue;
386 it->second.properties.push_back(propertyInfo); 225 classInfo->properties.push_back(propertyInfo);
387 } 226 }
388 227
389 void register_method(TYPEID classID, const char* name, 228 void register_method(TYPEID classID, const char* name,
390 const FunctionInfo& call) 229 const FunctionInfo& call)
391 { 230 {
392 auto it = classes.find(classID); 231 ClassInfo* classInfo = find_class(classID);
393 if (it == classes.end()) 232 if (!classInfo)
394 throw std::runtime_error(std::string("Method defined on unknown class: ") + name); 233 throw std::runtime_error(std::string("Method defined on unknown class: ") + name);
395 234
396 MethodInfo methodInfo; 235 MethodInfo methodInfo;
397 methodInfo.name = name; 236 methodInfo.name = name;
398 methodInfo.call = call; 237 methodInfo.call = call;
399 it->second.methods.push_back(methodInfo); 238 classInfo->methods.push_back(methodInfo);
400 } 239 }
401 240
402 void register_differentiator(TYPEID classID, size_t offset, 241 void register_differentiator(TYPEID classID, size_t offset,
403 std::vector<std::pair<int, std::string>>& mapping) 242 std::vector<std::pair<int, std::string>>& mapping)
404 { 243 {
405 auto it = classes.find(classID); 244 ClassInfo* classInfo = find_class(classID);
406 if (it == classes.end()) 245 if (!classInfo)
407 throw std::runtime_error("Subclass differentiator defined on unknown class "); 246 throw std::runtime_error("Subclass differentiator defined on unknown class ");
408 247
409 if (it->second.subclass_differentiator.offset != SIZE_MAX) 248 if (classInfo->subclass_differentiator.offset != SIZE_MAX)
410 throw std::runtime_error("More than one subclass differentiator defined fo r class " + it->second.name); 249 throw std::runtime_error("More than one subclass differentiator defined fo r class " + classInfo->name);
411 250
412 DifferentiatorInfo differentiatorInfo; 251 DifferentiatorInfo differentiatorInfo;
413 differentiatorInfo.offset = offset; 252 differentiatorInfo.offset = offset;
414 differentiatorInfo.mapping = mapping; 253 differentiatorInfo.mapping = mapping;
415 it->second.subclass_differentiator = differentiatorInfo; 254 classInfo->subclass_differentiator = differentiatorInfo;
416 } 255 }
417 256
418 const std::string generateCall(const FunctionInfo& call, 257 const std::string generateCall(const FunctionInfo& call,
419 std::vector<std::string>& params) 258 std::vector<std::string>& params)
420 { 259 {
421 if (call.returnType == TypeCategory::DEPENDENT_STRING || 260 if (call.returnType == TypeCategory::DEPENDENT_STRING ||
422 call.returnType == TypeCategory::OWNED_STRING) 261 call.returnType == TypeCategory::OWNED_STRING)
423 { 262 {
424 params.insert(params.begin(), "string"); 263 params.insert(params.begin(), "string");
425 } 264 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 } 298 }
460 case TypeCategory::STRING_REF: 299 case TypeCategory::STRING_REF:
461 return " var result = readString(" + call_str + ");\n"; 300 return " var result = readString(" + call_str + ");\n";
462 case TypeCategory::CLASS_PTR: 301 case TypeCategory::CLASS_PTR:
463 { 302 {
464 std::string result; 303 std::string result;
465 result += " var result = " + call_str + ";\n"; 304 result += " var result = " + call_str + ";\n";
466 result += " if (result)\n"; 305 result += " if (result)\n";
467 result += " {\n"; 306 result += " {\n";
468 307
469 auto it = classes.find(call.pointerType); 308 const ClassInfo* cls = find_class(call.pointerType);
470 if (it == classes.end()) 309 if (!cls)
471 throw std::runtime_error("Function " + std::string(call.name) + " retu rns pointer to unknown class"); 310 throw std::runtime_error("Function " + std::string(call.name) + " retu rns pointer to unknown class");
472 311
473 const ClassInfo& cls = it->second; 312 auto offset = cls->subclass_differentiator.offset;
474 auto offset = cls.subclass_differentiator.offset;
475 if (offset == SIZE_MAX) 313 if (offset == SIZE_MAX)
476 result += " result = exports." + cls.name + "(result);\n"; 314 result += " result = exports." + cls->name + "(result);\n";
477 else 315 else
478 result += " result = exports." + cls.name + ".fromPointer(result);\ n"; 316 result += " result = exports." + cls->name + ".fromPointer(result); \n";
479 317
480 result += " }\n"; 318 result += " }\n";
481 result += " else\n"; 319 result += " else\n";
482 result += " result = null;\n"; 320 result += " result = null;\n";
483 return result; 321 return result;
484 } 322 }
485 default: 323 default:
486 throw std::runtime_error("Unexpected return type for " + std::string(cal l.name)); 324 throw std::runtime_error("Unexpected return type for " + std::string(cal l.name));
487 } 325 }
488 } 326 }
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
592 { 430 {
593 Module._ReleaseRef(this._pointer + ref_counted_offset); 431 Module._ReleaseRef(this._pointer + ref_counted_offset);
594 }; 432 };
595 return result; 433 return result;
596 } 434 }
597 )"); 435 )");
598 } 436 }
599 437
600 void printClass(const ClassInfo& cls) 438 void printClass(const ClassInfo& cls)
601 { 439 {
440 ClassInfo* baseClass = find_class(cls.baseClass);
602 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), 441 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(),
603 (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "null"), 442 (baseClass ? ("exports." + baseClass->name).c_str() : "null"),
604 cls.ref_counted_offset); 443 cls.ref_counted_offset);
605 444
606 DifferentiatorInfo differentiator = cls.subclass_differentiator; 445 DifferentiatorInfo differentiator = cls.subclass_differentiator;
607 if (differentiator.offset != SIZE_MAX) 446 if (differentiator.offset != SIZE_MAX)
608 { 447 {
609 printf("var %s_mapping = \n", cls.name.c_str()); 448 printf("var %s_mapping = \n", cls.name.c_str());
610 puts("{"); 449 puts("{");
611 for (const auto& item : differentiator.mapping) 450 for (const auto& item : differentiator.mapping)
612 printf(" %i: '%s',\n", item.first, item.second.c_str()); 451 printf(" %i: '%s',\n", item.first, item.second.c_str());
613 puts("};"); 452 puts("};");
(...skipping 23 matching lines...) Expand all
637 476
638 for (const auto& item : cls.methods) 477 for (const auto& item : cls.methods)
639 { 478 {
640 std::string obj("exports." + cls.name); 479 std::string obj("exports." + cls.name);
641 if (item.call.instance_function) 480 if (item.call.instance_function)
642 obj += ".prototype"; 481 obj += ".prototype";
643 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), 482 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(),
644 wrapCall(item.call).c_str()); 483 wrapCall(item.call).c_str());
645 } 484 }
646 } 485 }
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 } 486 }
659 487
660 #if defined(PRINT_BINDINGS) 488 void printBindings()
661 // Bindings generation step: collect bindings information and print 489 {
662 // corresponding JS code. 490 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 491
720 template<typename ClassType, 492 for (const auto& item : classes)
721 typename BaseClass = bindings_internal::NoBaseClass, 493 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 494
739 template<typename FieldType> 495 for (const auto& item : customGenerators)
740 const class_& property(const char* name, 496 item();
741 FieldType (ClassType::*getter)() const, 497 }
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 498
801 void custom_generator(bindings_internal::CustomGenerator generator) 499 void custom_generator(bindings_internal::CustomGenerator generator)
802 { 500 {
803 bindings_internal::customGenerators.push_back(generator); 501 customGenerators.push_back(generator);
804 } 502 }
OLDNEW
« no previous file with comments | « compiled/bindings/generator.h ('k') | compiled/bindings/main.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld