Index: compiled/bindings/generator.h |
=================================================================== |
copy from compiled/bindings.ipp |
copy to compiled/bindings/generator.h |
--- a/compiled/bindings.ipp |
+++ b/compiled/bindings/generator.h |
@@ -14,30 +14,27 @@ |
* You should have received a copy of the GNU General Public License |
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
*/ |
#pragma once |
#include <cstddef> |
#include <cstdint> |
-#include <cstdio> |
#include <cstdlib> |
-#include <exception> |
#include <functional> |
-#include <map> |
#include <string> |
#include <type_traits> |
#include <utility> |
#include <vector> |
#include <emscripten.h> |
-#include "String.h" |
-#include "intrusive_ptr.h" |
+#include "../String.h" |
+#include "../intrusive_ptr.h" |
namespace bindings_internal |
{ |
typedef void* TYPEID; |
enum class TypeCategory |
{ |
UNKNOWN, |
@@ -117,82 +114,21 @@ namespace bindings_internal |
TypeCategory returnType; |
TYPEID pointerType; |
std::vector<TypeCategory> args; |
bool instance_function; |
int effectiveArgs; |
TypeCategory effectiveReturnType; |
char name[1024]; |
- FunctionInfo() |
- { |
- name[0] = '\0'; |
- } |
+ FunctionInfo(); |
FunctionInfo(TypeCategory returnType, TYPEID pointerType, |
std::initializer_list<TypeCategory> argTypes, bool instance_function, |
- void* function) |
- : returnType(returnType), pointerType(pointerType), |
- instance_function(instance_function) |
- { |
- name[0] = '\0'; |
- |
- // The function parameter is a pointer to the function pointer. |
- // Emscripten's "function pointers" are actually integers indicating the |
- // position in the call table. 0 represents nullptr. |
- if (!*reinterpret_cast<int*>(function)) |
- return; |
- |
- std::string signature; |
- if (returnType == TypeCategory::DEPENDENT_STRING || |
- returnType == TypeCategory::OWNED_STRING) |
- { |
- // Objects aren't really returned but passed as parameter. Note that |
- // this pointer might come before it but we don't care because both |
- // are integers (pointers) as far as Emscripten is concerned. |
- signature += "vi"; |
- } |
- else if (returnType == TypeCategory::VOID) |
- signature += 'v'; |
- else if (returnType == TypeCategory::FLOAT) |
- signature += 'd'; |
- else if (returnType == TypeCategory::INT || |
- returnType == TypeCategory::INT64 || |
- returnType == TypeCategory::STRING_REF || |
- returnType == TypeCategory::CLASS_PTR) |
- { |
- signature += 'i'; |
- } |
- else |
- throw std::runtime_error("Unexpected function return type"); |
- |
- if (instance_function) |
- { |
- // this pointer is an implicit parameter |
- signature += 'i'; |
- } |
- |
- for (const auto& item : argTypes) |
- { |
- if (item == TypeCategory::INT || item == TypeCategory::STRING_REF || |
- item == TypeCategory::CLASS_PTR) |
- { |
- signature += 'i'; |
- } |
- else if (item == TypeCategory::INT64) |
- signature += "ii"; |
- else if (item == TypeCategory::FLOAT) |
- signature += 'd'; |
- else |
- throw std::runtime_error("Unexpected function argument type"); |
- args.push_back(item); |
- } |
- |
- get_function_name(function, signature.c_str()); |
- } |
+ void* function); |
template<typename ReturnType, typename... Args> |
FunctionInfo(ReturnType (*function)(Args...)) |
: FunctionInfo(TypeInfo<ReturnType>(), |
TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, false, |
&function) |
{ |
} |
@@ -208,83 +144,19 @@ namespace bindings_internal |
template<typename ClassType, typename ReturnType, typename... Args> |
FunctionInfo(ReturnType (ClassType::*function)(Args...) const) |
: FunctionInfo(TypeInfo<ReturnType>(), |
TypeInfo<ReturnType>().pointer_type(), { TypeInfo<Args>()... }, true, |
&function) |
{ |
} |
- bool empty() const |
- { |
- return name[0] == '\0'; |
- } |
- |
- void get_function_name(void* ptr, const char* signature) |
- { |
- // This is a hack, C++ won't let us get the mangled function name. |
- // JavaScript is more dynamic so we pass the pointer to our function |
- // there. With that and the function signature we can call the function - |
- // with a full stack so that we will cause it to abort. Sometimes the |
- // function we are calling will also be missing from the build. The result |
- // is the same: abort() is called which in turn calls stackTrace(). By |
- // replacing stackTrace() we get access to the call stack and search it |
- // for the name of our function. |
- |
- EM_ASM_ARGS({ |
- var signature = AsciiToString($2); |
- var args = []; |
- for (var i = 1; i < signature.length; i++) |
- args.push(0); |
+ bool empty() const; |
- var oldPrint = Module.print; |
- var oldPrintErr = Module.printErr; |
- var oldStackTrace = stackTrace; |
- var sp = Runtime.stackSave(); |
- Module.print = function(){}; |
- Module.printErr = function(){}; |
- stackTrace = function() |
- { |
- var stack = []; |
- for (var f = arguments.callee.caller; f; f = f.caller) |
- { |
- if (f.name) |
- { |
- if (f.name.indexOf("dynCall") == 0) |
- break; |
- else |
- stack.push(f.name); |
- } |
- } |
- |
- result = stack[stack.length - 1]; |
- if (result && result.indexOf("__wrapper") >= 0) |
- result = stack[stack.length - 2]; |
- throw result; |
- }; |
- |
- Runtime.stackRestore(STACK_MAX); |
- |
- try |
- { |
- Runtime.dynCall(signature, HEAP32[$1 >> 2], args); |
- } |
- catch(e) |
- { |
- Module.stringToAscii(e, $0); |
- } |
- finally |
- { |
- Runtime.stackRestore(sp); |
- Module.print = oldPrint; |
- Module.printErr = oldPrintErr; |
- stackTrace = oldStackTrace; |
- } |
- }, name, ptr, signature); |
- } |
+ void get_function_name(void* ptr, const char* signature); |
}; |
class NoBaseClass |
{ |
}; |
struct PropertyInfo |
{ |
@@ -303,403 +175,51 @@ namespace bindings_internal |
struct DifferentiatorInfo |
{ |
size_t offset; |
std::vector<std::pair<int, std::string>> mapping; |
}; |
struct ClassInfo |
{ |
- ClassInfo* baseClass; |
+ TYPEID id; |
+ TYPEID baseClass; |
std::string name; |
std::vector<PropertyInfo> properties; |
std::vector<MethodInfo> methods; |
DifferentiatorInfo subclass_differentiator; |
ptrdiff_t ref_counted_offset; |
}; |
typedef std::function<void()> CustomGenerator; |
- std::map<TYPEID, ClassInfo> classes; |
- std::vector<CustomGenerator> customGenerators; |
- |
void register_class(const char* name, TYPEID classID, TYPEID baseClassID, |
- ptrdiff_t ref_counted_offset) |
- { |
- auto it = classes.find(classID); |
- if (it != classes.end()) |
- throw std::runtime_error(std::string("Duplicate definition for class ") + name); |
- |
- ClassInfo* baseClass = nullptr; |
- if (baseClassID != TypeInfo<NoBaseClass>()) |
- { |
- it = classes.find(baseClassID); |
- if (it == classes.end()) |
- throw std::runtime_error(std::string("Unknown base class defined for class ") + name); |
- baseClass = &(it->second); |
- } |
- |
- ClassInfo classInfo; |
- classInfo.baseClass = baseClass; |
- classInfo.name = name; |
- classInfo.subclass_differentiator.offset = SIZE_MAX; |
- classInfo.ref_counted_offset = ref_counted_offset; |
- classes[classID] = classInfo; |
- } |
+ ptrdiff_t ref_counted_offset); |
void register_property(TYPEID classID, const char* name, |
const FunctionInfo& getter, const FunctionInfo& setter, |
- const char* jsValue = "") |
- { |
- auto it = classes.find(classID); |
- if (it == classes.end()) |
- throw std::runtime_error(std::string("Property defined on unknown class: ") + name); |
- |
- PropertyInfo propertyInfo; |
- propertyInfo.name = name; |
- propertyInfo.getter = getter; |
- propertyInfo.setter = setter; |
- propertyInfo.jsValue = jsValue; |
- it->second.properties.push_back(propertyInfo); |
- } |
+ const char* jsValue = ""); |
void register_method(TYPEID classID, const char* name, |
- const FunctionInfo& call) |
- { |
- auto it = classes.find(classID); |
- if (it == classes.end()) |
- throw std::runtime_error(std::string("Method defined on unknown class: ") + name); |
- |
- MethodInfo methodInfo; |
- methodInfo.name = name; |
- methodInfo.call = call; |
- it->second.methods.push_back(methodInfo); |
- } |
+ const FunctionInfo& call); |
void register_differentiator(TYPEID classID, size_t offset, |
- std::vector<std::pair<int, std::string>>& mapping) |
- { |
- auto it = classes.find(classID); |
- if (it == classes.end()) |
- throw std::runtime_error("Subclass differentiator defined on unknown class"); |
- |
- if (it->second.subclass_differentiator.offset != SIZE_MAX) |
- throw std::runtime_error("More than one subclass differentiator defined for class " + it->second.name); |
- |
- DifferentiatorInfo differentiatorInfo; |
- differentiatorInfo.offset = offset; |
- differentiatorInfo.mapping = mapping; |
- it->second.subclass_differentiator = differentiatorInfo; |
- } |
+ std::vector<std::pair<int, std::string>>& mapping); |
const std::string generateCall(const FunctionInfo& call, |
- std::vector<std::string>& params) |
- { |
- if (call.returnType == TypeCategory::DEPENDENT_STRING || |
- call.returnType == TypeCategory::OWNED_STRING) |
- { |
- params.insert(params.begin(), "string"); |
- } |
- |
- std::string call_str(call.name); |
- call_str += "("; |
- for (int i = 0; i < params.size(); i++) |
- { |
- if (i > 0) |
- call_str += ", "; |
- call_str += params[i]; |
- } |
- call_str += ")"; |
- |
- if (call.returnType == TypeCategory::VOID) |
- return " " + call_str + ";\n"; |
- else if (call.returnType == TypeCategory::INT || |
- call.returnType == TypeCategory::FLOAT) |
- { |
- return " var result = " + call_str + ";\n"; |
- } |
- else if (call.returnType == TypeCategory::INT64) |
- { |
- // Emscripten saves the high bits of a 64-bit return value in a special |
- // variable called tempRet0. We cannot use bit operators to combine the |
- // values because JavaScript operates on 32-bit integers. |
- return " var result = (" + call_str + " >>> 0)" + |
- " + (Runtime.getTempRet0() >>> 0) * 0x100000000;\n"; |
- } |
- else if (call.returnType == TypeCategory::DEPENDENT_STRING || |
- call.returnType == TypeCategory::OWNED_STRING) |
- { |
- std::string result; |
- result += " var string = createString();\n"; |
- result += " " + call_str + ";\n"; |
- result += " var result = readString(string);\n"; |
- if (call.returnType == TypeCategory::OWNED_STRING) |
- result += " Module._DestroyString(string);\n"; |
- return result; |
- } |
- else if (call.returnType == TypeCategory::STRING_REF) |
- { |
- return " var result = readString(" + call_str + ");\n"; |
- } |
- else if (call.returnType == TypeCategory::CLASS_PTR) |
- { |
- std::string result; |
- result += " var result = " + call_str + ";\n"; |
- result += " if (result)\n"; |
- result += " {\n"; |
- |
- auto it = classes.find(call.pointerType); |
- if (it == classes.end()) |
- throw std::runtime_error("Function " + std::string(call.name) + " returns pointer to unknown class"); |
- |
- const ClassInfo& cls = it->second; |
- auto offset = cls.subclass_differentiator.offset; |
- if (offset == SIZE_MAX) |
- result += " result = exports." + cls.name + "(result);\n"; |
- else |
- result += " result = exports." + cls.name + ".fromPointer(result);\n"; |
- |
- result += " }\n"; |
- result += " else\n"; |
- result += " result = null;\n"; |
- return result; |
- } |
- else |
- throw std::runtime_error("Unexpected return type for " + std::string(call.name)); |
- } |
- |
- const std::string wrapCall(const FunctionInfo& call) |
- { |
- bool hasStringArgs = false; |
- std::vector<std::string> params; |
- std::string prefix = "function("; |
- for (int i = 0; i < call.args.size(); i++) |
- { |
- std::string argName("arg" + std::to_string(i)); |
- if (i > 0) |
- prefix += ", "; |
- prefix += argName; |
- |
- if (call.args[i] == TypeCategory::STRING_REF) |
- { |
- hasStringArgs = true; |
- params.push_back(std::string("createString(") + argName + ")"); |
- } |
- else if (call.args[i] == TypeCategory::CLASS_PTR) |
- params.push_back(argName + "._pointer"); |
- else if (call.args[i] == TypeCategory::INT64) |
- { |
- // 64-bit integers are passed as two integer parameters |
- params.push_back(argName + " >>> 0"); |
- params.push_back(argName + " / 0x100000000 >>> 0"); |
- } |
- else |
- params.push_back(argName); |
- } |
- prefix += ")\n{\n"; |
- |
- std::string suffix = "}"; |
- if (call.returnType != TypeCategory::VOID) |
- suffix = " return result;\n" + suffix; |
- |
- if (call.returnType == TypeCategory::DEPENDENT_STRING || |
- call.returnType == TypeCategory::OWNED_STRING || hasStringArgs) |
- { |
- prefix += " var sp = Runtime.stackSave();\n"; |
- suffix = " Runtime.stackRestore(sp);\n" + suffix; |
- } |
- |
- if (call.instance_function) |
- params.insert(params.begin(), "this._pointer"); |
- |
- return prefix + generateCall(call, params) + suffix; |
- } |
- |
- std::string generatePropertyDescriptor(const PropertyInfo& property) |
- { |
- if (!property.jsValue.empty()) |
- return "value: " + property.jsValue; |
- |
- std::string result("get: " + wrapCall(property.getter)); |
- if (!property.setter.empty()) |
- result += ", set: " + wrapCall(property.setter); |
- return result; |
- } |
- |
- void printHelpers() |
- { |
- printf("var sizeofString = %i;\n", sizeof(String)); |
- |
- puts(R"( |
- function copyString(str, buffer) |
- { |
- var length = str.length; |
- for (var i = 0, pointer = (buffer >> 1); i < length; i++, pointer++) |
- HEAP16[pointer] = str.charCodeAt(i); |
- return length; |
- } |
+ std::vector<std::string>& params); |
- function createString(str) |
- { |
- var length = 0; |
- var buffer = 0; |
- if (str) |
- { |
- buffer = Runtime.stackAlloc(str.length * 2); |
- length = copyString(str, buffer); |
- } |
- |
- var result = Runtime.stackAlloc(sizeofString); |
- Module._InitString(result, buffer, length); |
- return result; |
- } |
- |
- function readString(str) |
- { |
- var length = Module._GetStringLength(str); |
- var pointer = Module._GetStringData(str) >> 1; |
- return String.fromCharCode.apply(String, HEAP16.slice(pointer, pointer + length)); |
- } |
- |
- function createClass(superclass, ref_counted_offset) |
- { |
- var result = function(pointer) |
- { |
- this._pointer = pointer; |
- }; |
- if (superclass) |
- result.prototype = Object.create(superclass.prototype); |
- result.prototype.delete = function() |
- { |
- Module._ReleaseRef(this._pointer + ref_counted_offset); |
- }; |
- return result; |
- } |
- )"); |
- } |
- |
- void printClass(const ClassInfo& cls) |
- { |
- printf("exports.%s = createClass(%s, %i);\n", cls.name.c_str(), |
- (cls.baseClass ? ("exports." + cls.baseClass->name).c_str() : "null"), |
- cls.ref_counted_offset); |
- |
- DifferentiatorInfo differentiator = cls.subclass_differentiator; |
- if (differentiator.offset != SIZE_MAX) |
- { |
- printf("var %s_mapping = \n", cls.name.c_str()); |
- puts("{"); |
- for (const auto& item : differentiator.mapping) |
- printf(" %i: '%s',\n", item.first, item.second.c_str()); |
- puts("};"); |
- |
- printf("exports.%s.fromPointer = function(ptr)\n", cls.name.c_str()); |
- puts("{"); |
- printf(" var type = HEAP32[ptr + %i >> 2];\n", differentiator.offset); |
- printf(" if (type in %s_mapping)\n", cls.name.c_str()); |
- printf(" return new (exports[%s_mapping[type]])(ptr);\n", cls.name.c_str()); |
- printf(" throw new Error('Unexpected %s type: ' + type);\n", cls.name.c_str()); |
- puts("};"); |
- } |
- else |
- { |
- printf("exports.%s.fromPointer = function(ptr)\n", cls.name.c_str()); |
- puts("{"); |
- printf(" return new exports.%s(ptr);\n", cls.name.c_str()); |
- puts("};"); |
- } |
- |
- for (const auto& item : cls.properties) |
- { |
- printf("Object.defineProperty(exports.%s.prototype, '%s', {%s});\n", |
- cls.name.c_str(), item.name.c_str(), |
- generatePropertyDescriptor(item).c_str()); |
- } |
+ const std::string wrapCall(const FunctionInfo& call); |
- for (const auto& item : cls.methods) |
- { |
- std::string obj("exports." + cls.name); |
- if (item.call.instance_function) |
- obj += ".prototype"; |
- printf("%s.%s = %s;\n", obj.c_str(), item.name.c_str(), |
- wrapCall(item.call).c_str()); |
- } |
- } |
- |
- void printBindings() |
- { |
- printHelpers(); |
- |
- for (const auto& item : classes) |
- printClass(item.second); |
- |
- for (const auto& item : customGenerators) |
- item(); |
- } |
-} |
+ std::string generatePropertyDescriptor(const PropertyInfo& property); |
-#if defined(PRINT_BINDINGS) |
- // Bindings generation step: collect bindings information and print |
- // corresponding JS code. |
- #define EMSCRIPTEN_BINDINGS \ |
- void InitializeBindings();\ |
- int main()\ |
- {\ |
- try\ |
- {\ |
- InitializeBindings();\ |
- bindings_internal::printBindings();\ |
- }\ |
- catch (const std::exception& e)\ |
- {\ |
- EM_ASM_ARGS(\ |
- console.error("Error occurred generating JavaScript bindings: " +\ |
- Module.AsciiToString($0)), e.what()\ |
- );\ |
- abort();\ |
- }\ |
- return 0;\ |
- }\ |
- void InitializeBindings() |
-#else |
- // Actual compilation step: ignore bindings information but define some |
- // exported helper functions necessary for the bindings. |
- #define EMSCRIPTEN_BINDINGS \ |
- extern "C"\ |
- {\ |
- void EMSCRIPTEN_KEEPALIVE InitString(DependentString* str,\ |
- String::value_type* data, String::size_type len)\ |
- {\ |
- /* String is already allocated on stack, we merely need to call*/\ |
- /* constructor.*/\ |
- new (str) DependentString(data, len);\ |
- }\ |
- void EMSCRIPTEN_KEEPALIVE DestroyString(OwnedString* str)\ |
- {\ |
- /* Stack memory will be freed automatically, we need to call*/\ |
- /* destructor explicitly however.*/\ |
- str->~OwnedString();\ |
- }\ |
- String::size_type EMSCRIPTEN_KEEPALIVE GetStringLength(\ |
- const String& str)\ |
- {\ |
- return str.length();\ |
- }\ |
- const String::value_type* EMSCRIPTEN_KEEPALIVE GetStringData(\ |
- const String& str)\ |
- {\ |
- return str.data();\ |
- }\ |
- void EMSCRIPTEN_KEEPALIVE ReleaseRef(ref_counted* ptr)\ |
- {\ |
- ptr->ReleaseRef();\ |
- }\ |
- }\ |
- void BindingsInitializer_dummy() |
-#endif |
+ void printHelpers(); |
+ |
+ void printClass(const ClassInfo& cls); |
+} |
template<typename ClassType, |
typename BaseClass = bindings_internal::NoBaseClass, |
typename std::enable_if<std::is_base_of<ref_counted, ClassType>::value>::type* = nullptr> |
class class_ |
{ |
public: |
class_(const char* name) |
Wladimir Palant
2017/04/12 14:17:00
I would have preferred to move the implementations
|
@@ -772,12 +292,11 @@ public: |
mapping.emplace_back(item.first, item.second); |
bindings_internal::register_differentiator( |
bindings_internal::TypeInfo<ClassType>(), offset, mapping); |
return *this; |
} |
}; |
-void custom_generator(bindings_internal::CustomGenerator generator) |
-{ |
- bindings_internal::customGenerators.push_back(generator); |
-} |
+void custom_generator(bindings_internal::CustomGenerator generator); |
+ |
+void printBindings(); |