| 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 |