Left: | ||
Right: |
OLD | NEW |
---|---|
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; |
Wladimir Palant
2017/04/12 14:17:00
I found two bad assumptions here which went wrong
sergei
2017/04/18 10:46:09
That's correct, pointers and iterators are invalid
| |
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 DEPENDENT_STRING, | 39 instance_function(instance_function) |
49 OWNED_STRING, | 40 { |
50 STRING_REF, | 41 name[0] = '\0'; |
51 CLASS_PTR | 42 |
52 }; | 43 // The function parameter is a pointer to the function pointer. |
53 | 44 // Emscripten's "function pointers" are actually integers indicating the |
54 template<typename T> | 45 // position in the call table. 0 represents nullptr. |
55 struct TypeInfo | 46 if (!*reinterpret_cast<int*>(function)) |
56 { | 47 return; |
57 /* | 48 |
58 * Since TypeInfo is a templated type, in practice the compiler will define | 49 std::string signature; |
59 * a new type for each possible template parameter value. We use that fact | 50 if (returnType == TypeCategory::DEPENDENT_STRING || |
60 * to generate type identifiers: each of these TypeInfo types has a | 51 returnType == TypeCategory::OWNED_STRING) |
61 * different s_typeIDHelper member, so we use a pointer to that static | 52 { |
62 * variable as a type identifier - it will be different for each template | 53 // Objects aren't really returned but passed as parameter. Note that |
63 * parameter. | 54 // this pointer might come before it but we don't care because both |
64 */ | 55 // are integers (pointers) as far as Emscripten is concerned. |
65 static char s_typeIDHelper; | 56 signature += "vi"; |
66 constexpr operator TYPEID() const | 57 } |
67 { | 58 else if (returnType == TypeCategory::VOID) |
68 return &s_typeIDHelper; | 59 signature += 'v'; |
69 } | 60 else if (returnType == TypeCategory::FLOAT) |
70 | 61 signature += 'd'; |
71 constexpr operator TypeCategory() const | 62 else if (returnType == TypeCategory::INT || |
72 { | 63 returnType == TypeCategory::INT64 || |
73 if (std::is_void<T>()) | 64 returnType == TypeCategory::STRING_REF || |
74 return TypeCategory::VOID; | 65 returnType == TypeCategory::CLASS_PTR) |
75 | 66 { |
76 if (std::is_same<T, uint64_t>()) | 67 signature += 'i'; |
77 return TypeCategory::INT64; | 68 } |
78 | 69 else |
79 if (std::is_integral<T>() || std::is_enum<T>()) | 70 throw std::runtime_error("Unexpected function return type"); |
80 return TypeCategory::INT; | 71 |
81 | 72 if (instance_function) |
82 if (std::is_floating_point<T>()) | 73 { |
83 return TypeCategory::FLOAT; | 74 // this pointer is an implicit parameter |
84 | 75 signature += 'i'; |
85 if (std::is_same<DependentString, T>() || std::is_same<const DependentStri ng, T>()) | 76 } |
86 return TypeCategory::DEPENDENT_STRING; | 77 |
87 | 78 for (const auto& item : argTypes) |
88 if (std::is_same<OwnedString, T>() || std::is_same<const OwnedString, T>() ) | 79 { |
89 return TypeCategory::OWNED_STRING; | 80 if (item == TypeCategory::INT || item == TypeCategory::STRING_REF || |
90 | 81 item == TypeCategory::CLASS_PTR) |
91 if (std::is_same<String&, T>() || std::is_same<const String&, T>() || | 82 { |
92 std::is_same<DependentString&, T>()) | 83 signature += 'i'; |
93 { | 84 } |
94 return TypeCategory::STRING_REF; | 85 else if (item == TypeCategory::INT64) |
95 } | 86 signature += "ii"; |
96 | 87 else if (item == TypeCategory::FLOAT) |
97 if (std::is_pointer<T>() && std::is_class<typename std::remove_pointer<T>: :type>()) | 88 signature += 'd'; |
98 return TypeCategory::CLASS_PTR; | |
99 | |
100 return TypeCategory::UNKNOWN; | |
101 } | |
102 | |
103 constexpr TYPEID pointer_type() const | |
104 { | |
105 if (std::is_pointer<T>()) | |
106 return TypeInfo<typename std::remove_pointer<T>::type>(); | |
107 else | 89 else |
108 return nullptr; | 90 throw std::runtime_error("Unexpected function argument type"); |
109 } | 91 args.push_back(item); |
110 }; | 92 } |
111 | 93 |
112 template<typename T> | 94 get_function_name(function, signature.c_str()); |
113 char TypeInfo<T>::s_typeIDHelper; | 95 } |
114 | 96 |
115 struct FunctionInfo | 97 bool FunctionInfo::empty() const |
116 { | 98 { |
117 TypeCategory returnType; | 99 return name[0] == '\0'; |
118 TYPEID pointerType; | 100 } |
119 std::vector<TypeCategory> args; | 101 |
120 bool instance_function; | 102 void FunctionInfo::get_function_name(void* ptr, const char* signature) |
121 int effectiveArgs; | 103 { |
122 TypeCategory effectiveReturnType; | 104 // This is a hack, C++ won't let us get the mangled function name. |
123 char name[1024]; | 105 // JavaScript is more dynamic so we pass the pointer to our function |
124 | 106 // there. With that and the function signature we can call the function - |
125 FunctionInfo() | 107 // with a full stack so that we will cause it to abort. Sometimes the |
126 { | 108 // function we are calling will also be missing from the build. The result |
127 name[0] = '\0'; | 109 // is the same: abort() is called which in turn calls stackTrace(). By |
128 } | 110 // replacing stackTrace() we get access to the call stack and search it |
129 | 111 // for the name of our function. |
130 FunctionInfo(TypeCategory returnType, TYPEID pointerType, | 112 |
131 std::initializer_list<TypeCategory> argTypes, bool instance_function, | 113 EM_ASM_ARGS({ |
132 void* function) | 114 var signature = AsciiToString($2); |
133 : returnType(returnType), pointerType(pointerType), | 115 var args = []; |
134 instance_function(instance_function) | 116 for (var i = 1; i < signature.length; i++) |
135 { | 117 args.push(0); |
136 name[0] = '\0'; | 118 |
137 | 119 var oldPrint = Module.print; |
138 // The function parameter is a pointer to the function pointer. | 120 var oldPrintErr = Module.printErr; |
139 // Emscripten's "function pointers" are actually integers indicating the | 121 var oldStackTrace = stackTrace; |
140 // position in the call table. 0 represents nullptr. | 122 var sp = Runtime.stackSave(); |
141 if (!*reinterpret_cast<int*>(function)) | 123 Module.print = function(){}; |
142 return; | 124 Module.printErr = function(){}; |
143 | 125 stackTrace = function() |
144 std::string signature; | 126 { |
145 if (returnType == TypeCategory::DEPENDENT_STRING || | 127 var stack = []; |
146 returnType == TypeCategory::OWNED_STRING) | 128 for (var f = arguments.callee.caller; f; f = f.caller) |
147 { | |
148 // Objects aren't really returned but passed as parameter. Note that | |
149 // this pointer might come before it but we don't care because both | |
150 // are integers (pointers) as far as Emscripten is concerned. | |
151 signature += "vi"; | |
152 } | |
153 else if (returnType == TypeCategory::VOID) | |
154 signature += 'v'; | |
155 else if (returnType == TypeCategory::FLOAT) | |
156 signature += 'd'; | |
157 else if (returnType == TypeCategory::INT || | |
158 returnType == TypeCategory::INT64 || | |
159 returnType == TypeCategory::STRING_REF || | |
160 returnType == TypeCategory::CLASS_PTR) | |
161 { | |
162 signature += 'i'; | |
163 } | |
164 else | |
165 throw std::runtime_error("Unexpected function return type"); | |
166 | |
167 if (instance_function) | |
168 { | |
169 // this pointer is an implicit parameter | |
170 signature += 'i'; | |
171 } | |
172 | |
173 for (const auto& item : argTypes) | |
174 { | |
175 if (item == TypeCategory::INT || item == TypeCategory::STRING_REF || | |
176 item == TypeCategory::CLASS_PTR) | |
177 { | 129 { |
178 signature += 'i'; | 130 if (f.name) |
131 { | |
132 if (f.name.indexOf("dynCall") == 0) | |
133 break; | |
134 else | |
135 stack.push(f.name); | |
136 } | |
179 } | 137 } |
180 else if (item == TypeCategory::INT64) | 138 |
181 signature += "ii"; | 139 result = stack[stack.length - 1]; |
182 else if (item == TypeCategory::FLOAT) | 140 if (result && result.indexOf("__wrapper") >= 0) |
183 signature += 'd'; | 141 result = stack[stack.length - 2]; |
184 else | 142 throw result; |
185 throw std::runtime_error("Unexpected function argument type"); | 143 }; |
186 args.push_back(item); | 144 |
187 } | 145 Runtime.stackRestore(STACK_MAX); |
188 | 146 |
189 get_function_name(function, signature.c_str()); | 147 try |
190 } | 148 { |
191 | 149 Runtime.dynCall(signature, HEAP32[$1 >> 2], args); |
192 template<typename ReturnType, typename... Args> | 150 } |
193 FunctionInfo(ReturnType (*function)(Args...)) | 151 catch(e) |
194 : FunctionInfo(TypeInfo<ReturnType>(), | 152 { |
195 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, | 153 Module.stringToAscii(e, $0); |
196 &function) | 154 } |
197 { | 155 finally |
198 } | 156 { |
199 | 157 Runtime.stackRestore(sp); |
200 template<typename ClassType, typename ReturnType, typename... Args> | 158 Module.print = oldPrint; |
201 FunctionInfo(ReturnType (ClassType::*function)(Args...)) | 159 Module.printErr = oldPrintErr; |
202 : FunctionInfo(TypeInfo<ReturnType>(), | 160 stackTrace = oldStackTrace; |
203 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 161 } |
204 &function) | 162 }, name, ptr, signature); |
205 { | 163 } |
206 } | 164 |
207 | 165 ClassInfo* find_class(TYPEID classID) |
208 template<typename ClassType, typename ReturnType, typename... Args> | 166 { |
209 FunctionInfo(ReturnType (ClassType::*function)(Args...) const) | 167 for (auto& classInfo : classes) |
210 : FunctionInfo(TypeInfo<ReturnType>(), | 168 if (classInfo.id == classID) |
211 TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, | 169 return &classInfo; |
212 &function) | 170 return nullptr; |
213 { | 171 } |
214 } | |
215 | |
216 bool empty() const | |
217 { | |
218 return name[0] == '\0'; | |
219 } | |
220 | |
221 void get_function_name(void* ptr, const char* signature) | |
222 { | |
223 // This is a hack, C++ won't let us get the mangled function name. | |
224 // JavaScript is more dynamic so we pass the pointer to our function | |
225 // there. With that and the function signature we can call the function - | |
226 // with a full stack so that we will cause it to abort. Sometimes the | |
227 // function we are calling will also be missing from the build. The result | |
228 // is the same: abort() is called which in turn calls stackTrace(). By | |
229 // replacing stackTrace() we get access to the call stack and search it | |
230 // for the name of our function. | |
231 | |
232 EM_ASM_ARGS({ | |
233 var signature = AsciiToString($2); | |
234 var args = []; | |
235 for (var i = 1; i < signature.length; i++) | |
236 args.push(0); | |
237 | |
238 var oldPrint = Module.print; | |
239 var oldPrintErr = Module.printErr; | |
240 var oldStackTrace = stackTrace; | |
241 var sp = Runtime.stackSave(); | |
242 Module.print = function(){}; | |
243 Module.printErr = function(){}; | |
244 stackTrace = function() | |
245 { | |
246 var stack = []; | |
247 for (var f = arguments.callee.caller; f; f = f.caller) | |
248 { | |
249 if (f.name) | |
250 { | |
251 if (f.name.indexOf("dynCall") == 0) | |
252 break; | |
253 else | |
254 stack.push(f.name); | |
255 } | |
256 } | |
257 | |
258 result = stack[stack.length - 1]; | |
259 if (result && result.indexOf("__wrapper") >= 0) | |
260 result = stack[stack.length - 2]; | |
261 throw result; | |
262 }; | |
263 | |
264 Runtime.stackRestore(STACK_MAX); | |
265 | |
266 try | |
267 { | |
268 Runtime.dynCall(signature, HEAP32[$1 >> 2], args); | |
269 } | |
270 catch(e) | |
271 { | |
272 Module.stringToAscii(e, $0); | |
273 } | |
274 finally | |
275 { | |
276 Runtime.stackRestore(sp); | |
277 Module.print = oldPrint; | |
278 Module.printErr = oldPrintErr; | |
279 stackTrace = oldStackTrace; | |
280 } | |
281 }, name, ptr, signature); | |
282 } | |
283 }; | |
284 | |
285 class NoBaseClass | |
286 { | |
287 }; | |
288 | |
289 struct PropertyInfo | |
290 { | |
291 std::string name; | |
292 FunctionInfo getter; | |
293 FunctionInfo setter; | |
294 std::string jsValue; | |
295 }; | |
296 | |
297 struct MethodInfo | |
298 { | |
299 std::string name; | |
300 FunctionInfo call; | |
301 }; | |
302 | |
303 struct DifferentiatorInfo | |
304 { | |
305 size_t offset; | |
306 std::vector<std::pair<int, std::string>> mapping; | |
307 }; | |
308 | |
309 struct ClassInfo | |
310 { | |
311 ClassInfo* baseClass; | |
312 std::string name; | |
313 std::vector<PropertyInfo> properties; | |
314 std::vector<MethodInfo> methods; | |
315 DifferentiatorInfo subclass_differentiator; | |
316 ptrdiff_t ref_counted_offset; | |
317 }; | |
318 | |
319 typedef std::function<void()> CustomGenerator; | |
320 | |
321 std::map<TYPEID, ClassInfo> classes; | |
322 std::vector<CustomGenerator> customGenerators; | |
323 | 172 |
324 void register_class(const char* name, TYPEID classID, TYPEID baseClassID, | 173 void register_class(const char* name, TYPEID classID, TYPEID baseClassID, |
325 ptrdiff_t ref_counted_offset) | 174 ptrdiff_t ref_counted_offset) |
326 { | 175 { |
327 auto it = classes.find(classID); | 176 if (find_class(classID)) |
328 if (it != classes.end()) | |
329 throw std::runtime_error(std::string("Duplicate definition for class ") + name); | 177 throw std::runtime_error(std::string("Duplicate definition for class ") + name); |
330 | 178 |
331 ClassInfo* baseClass = nullptr; | 179 if (baseClassID != TypeInfo<NoBaseClass>() && !find_class(baseClassID)) |
332 if (baseClassID != TypeInfo<NoBaseClass>()) | 180 throw std::runtime_error(std::string("Unknown base class defined for class ") + name); |
333 { | |
334 it = classes.find(baseClassID); | |
335 if (it == classes.end()) | |
336 throw std::runtime_error(std::string("Unknown base class defined for cla ss ") + name); | |
337 baseClass = &(it->second); | |
338 } | |
339 | 181 |
340 ClassInfo classInfo; | 182 ClassInfo classInfo; |
341 classInfo.baseClass = baseClass; | 183 classInfo.id = classID; |
184 classInfo.baseClass = baseClassID; | |
342 classInfo.name = name; | 185 classInfo.name = name; |
343 classInfo.subclass_differentiator.offset = SIZE_MAX; | 186 classInfo.subclass_differentiator.offset = SIZE_MAX; |
344 classInfo.ref_counted_offset = ref_counted_offset; | 187 classInfo.ref_counted_offset = ref_counted_offset; |
345 classes[classID] = classInfo; | 188 classes.push_back(classInfo); |
346 } | 189 } |
347 | 190 |
348 void register_property(TYPEID classID, const char* name, | 191 void register_property(TYPEID classID, const char* name, |
349 const FunctionInfo& getter, const FunctionInfo& setter, | 192 const FunctionInfo& getter, const FunctionInfo& setter, |
350 const char* jsValue = "") | 193 const char* jsValue) |
351 { | 194 { |
352 auto it = classes.find(classID); | 195 ClassInfo* classInfo = find_class(classID); |
353 if (it == classes.end()) | 196 if (!classInfo) |
354 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name); | 197 throw std::runtime_error(std::string("Property defined on unknown class: " ) + name); |
355 | 198 |
356 PropertyInfo propertyInfo; | 199 PropertyInfo propertyInfo; |
357 propertyInfo.name = name; | 200 propertyInfo.name = name; |
358 propertyInfo.getter = getter; | 201 propertyInfo.getter = getter; |
359 propertyInfo.setter = setter; | 202 propertyInfo.setter = setter; |
360 propertyInfo.jsValue = jsValue; | 203 propertyInfo.jsValue = jsValue; |
361 it->second.properties.push_back(propertyInfo); | 204 classInfo->properties.push_back(propertyInfo); |
362 } | 205 } |
363 | 206 |
364 void register_method(TYPEID classID, const char* name, | 207 void register_method(TYPEID classID, const char* name, |
365 const FunctionInfo& call) | 208 const FunctionInfo& call) |
366 { | 209 { |
367 auto it = classes.find(classID); | 210 ClassInfo* classInfo = find_class(classID); |
368 if (it == classes.end()) | 211 if (!classInfo) |
369 throw std::runtime_error(std::string("Method defined on unknown class: ") + name); | 212 throw std::runtime_error(std::string("Method defined on unknown class: ") + name); |
370 | 213 |
371 MethodInfo methodInfo; | 214 MethodInfo methodInfo; |
372 methodInfo.name = name; | 215 methodInfo.name = name; |
373 methodInfo.call = call; | 216 methodInfo.call = call; |
374 it->second.methods.push_back(methodInfo); | 217 classInfo->methods.push_back(methodInfo); |
375 } | 218 } |
376 | 219 |
377 void register_differentiator(TYPEID classID, size_t offset, | 220 void register_differentiator(TYPEID classID, size_t offset, |
378 std::vector<std::pair<int, std::string>>& mapping) | 221 std::vector<std::pair<int, std::string>>& mapping) |
379 { | 222 { |
380 auto it = classes.find(classID); | 223 ClassInfo* classInfo = find_class(classID); |
381 if (it == classes.end()) | 224 if (!classInfo) |
382 throw std::runtime_error("Subclass differentiator defined on unknown class "); | 225 throw std::runtime_error("Subclass differentiator defined on unknown class "); |
383 | 226 |
384 if (it->second.subclass_differentiator.offset != SIZE_MAX) | 227 if (classInfo->subclass_differentiator.offset != SIZE_MAX) |
385 throw std::runtime_error("More than one subclass differentiator defined fo r class " + it->second.name); | 228 throw std::runtime_error("More than one subclass differentiator defined fo r class " + classInfo->name); |
386 | 229 |
387 DifferentiatorInfo differentiatorInfo; | 230 DifferentiatorInfo differentiatorInfo; |
388 differentiatorInfo.offset = offset; | 231 differentiatorInfo.offset = offset; |
389 differentiatorInfo.mapping = mapping; | 232 differentiatorInfo.mapping = mapping; |
390 it->second.subclass_differentiator = differentiatorInfo; | 233 classInfo->subclass_differentiator = differentiatorInfo; |
391 } | 234 } |
392 | 235 |
393 const std::string generateCall(const FunctionInfo& call, | 236 const std::string generateCall(const FunctionInfo& call, |
394 std::vector<std::string>& params) | 237 std::vector<std::string>& params) |
395 { | 238 { |
396 if (call.returnType == TypeCategory::DEPENDENT_STRING || | 239 if (call.returnType == TypeCategory::DEPENDENT_STRING || |
397 call.returnType == TypeCategory::OWNED_STRING) | 240 call.returnType == TypeCategory::OWNED_STRING) |
398 { | 241 { |
399 params.insert(params.begin(), "string"); | 242 params.insert(params.begin(), "string"); |
400 } | 243 } |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
439 { | 282 { |
440 return " var result = readString(" + call_str + ");\n"; | 283 return " var result = readString(" + call_str + ");\n"; |
441 } | 284 } |
442 else if (call.returnType == TypeCategory::CLASS_PTR) | 285 else if (call.returnType == TypeCategory::CLASS_PTR) |
443 { | 286 { |
444 std::string result; | 287 std::string result; |
445 result += " var result = " + call_str + ";\n"; | 288 result += " var result = " + call_str + ";\n"; |
446 result += " if (result)\n"; | 289 result += " if (result)\n"; |
447 result += " {\n"; | 290 result += " {\n"; |
448 | 291 |
449 auto it = classes.find(call.pointerType); | 292 const ClassInfo* cls = find_class(call.pointerType); |
450 if (it == classes.end()) | 293 if (!cls) |
451 throw std::runtime_error("Function " + std::string(call.name) + " return s pointer to unknown class"); | 294 throw std::runtime_error("Function " + std::string(call.name) + " return s pointer to unknown class"); |
452 | 295 |
453 const ClassInfo& cls = it->second; | 296 auto offset = cls->subclass_differentiator.offset; |
454 auto offset = cls.subclass_differentiator.offset; | |
455 if (offset == SIZE_MAX) | 297 if (offset == SIZE_MAX) |
456 result += " result = exports." + cls.name + "(result);\n"; | 298 result += " result = exports." + cls->name + "(result);\n"; |
457 else | 299 else |
458 result += " result = exports." + cls.name + ".fromPointer(result);\n" ; | 300 result += " result = exports." + cls->name + ".fromPointer(result);\n "; |
459 | 301 |
460 result += " }\n"; | 302 result += " }\n"; |
461 result += " else\n"; | 303 result += " else\n"; |
462 result += " result = null;\n"; | 304 result += " result = null;\n"; |
463 return result; | 305 return result; |
464 } | 306 } |
465 else | 307 else |
466 throw std::runtime_error("Unexpected return type for " + std::string(call. name)); | 308 throw std::runtime_error("Unexpected return type for " + std::string(call. name)); |
467 } | 309 } |
468 | 310 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
571 { | 413 { |
572 Module._ReleaseRef(this._pointer + ref_counted_offset); | 414 Module._ReleaseRef(this._pointer + ref_counted_offset); |
573 }; | 415 }; |
574 return result; | 416 return result; |
575 } | 417 } |
576 )"); | 418 )"); |
577 } | 419 } |
578 | 420 |
579 void printClass(const ClassInfo& cls) | 421 void printClass(const ClassInfo& cls) |
580 { | 422 { |
423 ClassInfo* baseClass = find_class(cls.baseClass); | |
581 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), | 424 printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), |
582 (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "null"), | 425 (baseClass ? ("exports." + baseClass->name).c_str() : "null"), |
583 cls.ref_counted_offset); | 426 cls.ref_counted_offset); |
584 | 427 |
585 DifferentiatorInfo differentiator = cls.subclass_differentiator; | 428 DifferentiatorInfo differentiator = cls.subclass_differentiator; |
586 if (differentiator.offset != SIZE_MAX) | 429 if (differentiator.offset != SIZE_MAX) |
587 { | 430 { |
588 printf("var %s_mapping = \n", cls.name.c_str()); | 431 printf("var %s_mapping = \n", cls.name.c_str()); |
589 puts("{"); | 432 puts("{"); |
590 for (const auto& item : differentiator.mapping) | 433 for (const auto& item : differentiator.mapping) |
591 printf(" %i: '%s',\n", item.first, item.second.c_str()); | 434 printf(" %i: '%s',\n", item.first, item.second.c_str()); |
592 puts("};"); | 435 puts("};"); |
(...skipping 23 matching lines...) Expand all Loading... | |
616 | 459 |
617 for (const auto& item : cls.methods) | 460 for (const auto& item : cls.methods) |
618 { | 461 { |
619 std::string obj("exports." + cls.name); | 462 std::string obj("exports." + cls.name); |
620 if (item.call.instance_function) | 463 if (item.call.instance_function) |
621 obj += ".prototype"; | 464 obj += ".prototype"; |
622 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), | 465 printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), |
623 wrapCall(item.call).c_str()); | 466 wrapCall(item.call).c_str()); |
624 } | 467 } |
625 } | 468 } |
626 | |
627 void printBindings() | |
628 { | |
629 printHelpers(); | |
630 | |
631 for (const auto& item : classes) | |
632 printClass(item.second); | |
633 | |
634 for (const auto& item : customGenerators) | |
635 item(); | |
636 } | |
637 } | 469 } |
638 | 470 |
639 #if defined(PRINT_BINDINGS) | 471 void printBindings() |
640 // Bindings generation step: collect bindings information and print | 472 { |
641 // corresponding JS code. | 473 bindings_internal::printHelpers(); |
642 #define EMSCRIPTEN_BINDINGS \ | |
643 void InitializeBindings();\ | |
644 int main()\ | |
645 {\ | |
646 try\ | |
647 {\ | |
648 InitializeBindings();\ | |
649 bindings_internal::printBindings();\ | |
650 }\ | |
651 catch (const std::exception& e)\ | |
652 {\ | |
653 EM_ASM_ARGS(\ | |
654 console.error("Error occurred generating JavaScript bindings: " +\ | |
655 Module.AsciiToString($0)), e.what()\ | |
656 );\ | |
657 abort();\ | |
658 }\ | |
659 return 0;\ | |
660 }\ | |
661 void InitializeBindings() | |
662 #else | |
663 // Actual compilation step: ignore bindings information but define some | |
664 // exported helper functions necessary for the bindings. | |
665 #define EMSCRIPTEN_BINDINGS \ | |
666 extern "C"\ | |
667 {\ | |
668 void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ | |
669 String::value_type* data, String::size_type len)\ | |
670 {\ | |
671 /* String is already allocated on stack, we merely need to call*/\ | |
672 /* constructor.*/\ | |
673 new (str) DependentString(data, len);\ | |
674 }\ | |
675 void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\ | |
676 {\ | |
677 /* Stack memory will be freed automatically, we need to call*/\ | |
678 /* destructor explicitly however.*/\ | |
679 str->~OwnedString();\ | |
680 }\ | |
681 String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\ | |
682 const String& str)\ | |
683 {\ | |
684 return str.length();\ | |
685 }\ | |
686 const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\ | |
687 const String& str)\ | |
688 {\ | |
689 return str.data();\ | |
690 }\ | |
691 void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\ | |
692 {\ | |
693 ptr->ReleaseRef();\ | |
694 }\ | |
695 }\ | |
696 void BindingsInitializer_dummy() | |
697 #endif | |
698 | 474 |
699 template<typename ClassType, | 475 for (const auto& item : classes) |
700 typename BaseClass = bindings_internal::NoBaseClass, | 476 bindings_internal::printClass(item); |
701 typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::typ e* = nullptr> | |
702 class class_ | |
703 { | |
704 public: | |
705 class_(const char* name) | |
706 { | |
707 ClassType* ptr = reinterpret_cast<ClassType*>(0x10000000); | |
708 ptrdiff_t ref_counted_offset = | |
709 reinterpret_cast<char*>(static_cast<ref_counted*>(ptr)) - | |
710 reinterpret_cast<char*>(ptr); | |
711 bindings_internal::register_class(name, | |
712 bindings_internal::TypeInfo<ClassType>(), | |
713 bindings_internal::TypeInfo<BaseClass>(), | |
714 ref_counted_offset | |
715 ); | |
716 } | |
717 | 477 |
718 template<typename FieldType> | 478 for (const auto& item : customGenerators) |
719 const class_& property(const char* name, | 479 item(); |
720 FieldType (ClassType::*getter)() const, | 480 } |
721 void (ClassType::*setter)(FieldType) = nullptr) const | |
722 { | |
723 bindings_internal::register_property( | |
724 bindings_internal::TypeInfo<ClassType>(), name, getter, setter); | |
725 return *this; | |
726 } | |
727 | |
728 const class_& class_property(const char* name, | |
729 const char* jsValue) const | |
730 { | |
731 bindings_internal::register_property( | |
732 bindings_internal::TypeInfo<ClassType>(), name, | |
733 bindings_internal::FunctionInfo(), bindings_internal::FunctionInfo(), | |
734 jsValue); | |
735 return *this; | |
736 } | |
737 | |
738 template<typename ReturnType, typename... Args> | |
739 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..)) const | |
740 { | |
741 bindings_internal::register_method( | |
742 bindings_internal::TypeInfo<ClassType>(), name, method); | |
743 return *this; | |
744 } | |
745 | |
746 template<typename ReturnType, typename... Args> | |
747 const class_& function(const char* name, ReturnType (ClassType::*method)(Args. ..) const) const | |
748 { | |
749 bindings_internal::register_method( | |
750 bindings_internal::TypeInfo<ClassType>(), name, method); | |
751 return *this; | |
752 } | |
753 | |
754 template<typename ReturnType, typename... Args> | |
755 const class_& class_function(const char* name, ReturnType (*method)(Args...)) const | |
756 { | |
757 bindings_internal::register_method( | |
758 bindings_internal::TypeInfo<ClassType>(), name, method); | |
759 return *this; | |
760 } | |
761 | |
762 template<typename ReturnType, | |
763 typename std::enable_if<std::is_convertible<ReturnType, int32_t>::value>:: type* = nullptr> | |
764 const class_& subclass_differentiator(ReturnType ClassType::* member, | |
765 std::initializer_list<std::pair<ReturnType, const char*>> list) const | |
766 { | |
767 ClassType* instance = nullptr; | |
768 size_t offset = (char*)&(instance->*member) - (char*)instance; | |
769 | |
770 std::vector<std::pair<int, std::string>> mapping; | |
771 for (const auto& item : list) | |
772 mapping.emplace_back(item.first, item.second); | |
773 | |
774 bindings_internal::register_differentiator( | |
775 bindings_internal::TypeInfo<ClassType>(), offset, mapping); | |
776 return *this; | |
777 } | |
778 }; | |
779 | 481 |
780 void custom_generator(bindings_internal::CustomGenerator generator) | 482 void custom_generator(bindings_internal::CustomGenerator generator) |
781 { | 483 { |
782 bindings_internal::customGenerators.push_back(generator); | 484 customGenerators.push_back(generator); |
783 } | 485 } |
OLD | NEW |