Merge pull request #592 from imvu/embind-pull-request
Embind pull request
This commit is contained in:
Коммит
375982d9c1
|
@ -0,0 +1,619 @@
|
|||
/*global Module*/
|
||||
/*global _malloc, _free, _memcpy*/
|
||||
/*global FUNCTION_TABLE, HEAP32*/
|
||||
/*global Pointer_stringify, writeStringToMemory*/
|
||||
/*global __emval_register, _emval_handle_array, __emval_decref*/
|
||||
|
||||
function createNamedFunction(name, body) {
|
||||
/*jshint evil:true*/
|
||||
return new Function(
|
||||
"body",
|
||||
"return function " + name + "() {\n" +
|
||||
" return body.apply(this, arguments);\n" +
|
||||
"};\n"
|
||||
)(body);
|
||||
}
|
||||
|
||||
function _embind_repr(v) {
|
||||
var t = typeof v;
|
||||
if (t === 'object' || t === 'array' || t === 'function') {
|
||||
return v.toString();
|
||||
} else {
|
||||
return '' + v;
|
||||
}
|
||||
}
|
||||
|
||||
var typeRegistry = {};
|
||||
|
||||
function validateType(type, name) {
|
||||
if (!type) {
|
||||
throw new BindingError('type "' + name + '" must have a positive integer typeid pointer');
|
||||
}
|
||||
if (undefined !== typeRegistry[type]) {
|
||||
throw new BindingError('cannot register type "' + name + '" twice');
|
||||
}
|
||||
}
|
||||
|
||||
function __embind_register_void(voidType, name) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(voidType, name);
|
||||
typeRegistry[voidType] = {
|
||||
name: name,
|
||||
fromWireType: function() {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_bool(boolType, name, trueValue, falseValue) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(boolType, name);
|
||||
typeRegistry[boolType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, o) {
|
||||
return o ? trueValue : falseValue;
|
||||
},
|
||||
fromWireType: function(wt) {
|
||||
return wt === trueValue;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_integer(primitiveType, name) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(primitiveType, name);
|
||||
typeRegistry[primitiveType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, value) {
|
||||
if (typeof value !== "number") {
|
||||
throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
|
||||
}
|
||||
return value | 0;
|
||||
},
|
||||
fromWireType: function(value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_float(primitiveType, name) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(primitiveType, name);
|
||||
typeRegistry[primitiveType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, value) {
|
||||
if (typeof value !== "number") {
|
||||
throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + name);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
fromWireType: function(value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_cstring(stringType, name) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(stringType, name);
|
||||
typeRegistry[stringType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, value) {
|
||||
var ptr = _malloc(value.length + 1);
|
||||
writeStringToMemory(value, ptr);
|
||||
destructors.push(_free);
|
||||
destructors.push(ptr);
|
||||
return ptr;
|
||||
},
|
||||
fromWireType: function(value) {
|
||||
var rv = Pointer_stringify(value);
|
||||
_free(value);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_emval(emvalType, name) {
|
||||
name = Pointer_stringify(name);
|
||||
validateType(emvalType, name);
|
||||
typeRegistry[emvalType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, value) {
|
||||
return __emval_register(value);
|
||||
},
|
||||
fromWireType: function(handle) {
|
||||
var rv = _emval_handle_array[handle].value;
|
||||
__emval_decref(handle);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var BindingError = Error;
|
||||
/** @expose */
|
||||
Module.BindingError = BindingError;
|
||||
|
||||
function typeName(typeID) {
|
||||
// could use our carnal knowledge of RTTI but for now just return the pointer...
|
||||
return typeID;
|
||||
}
|
||||
|
||||
function requireRegisteredType(type, humanName) {
|
||||
var impl = typeRegistry[type];
|
||||
if (undefined === impl) {
|
||||
throw new BindingError(humanName + " has unknown type: " + typeName(type));
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
function requireArgumentTypes(argCount, argTypes, name) {
|
||||
var argTypeImpls = new Array(argCount);
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
var argType = HEAP32[(argTypes >> 2) + i];
|
||||
argTypeImpls[i] = requireRegisteredType(argType, name + " parameter " + i);
|
||||
}
|
||||
return argTypeImpls;
|
||||
}
|
||||
|
||||
function runDestructors(destructors) {
|
||||
while (destructors.length) {
|
||||
var ptr = destructors.pop();
|
||||
var del = destructors.pop();
|
||||
del(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
function __embind_register_function(name, returnType, argCount, argTypes, invoker, fn) {
|
||||
name = Pointer_stringify(name);
|
||||
returnType = requireRegisteredType(returnType, "Function " + name + " return value");
|
||||
invoker = FUNCTION_TABLE[invoker];
|
||||
argTypes = requireArgumentTypes(argCount, argTypes, name);
|
||||
|
||||
Module[name] = function() {
|
||||
if (arguments.length !== argCount) {
|
||||
throw new BindingError('emscripten binding function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + argCount);
|
||||
}
|
||||
var destructors = [];
|
||||
var args = new Array(argCount + 1);
|
||||
args[0] = fn;
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
args[i + 1] = argTypes[i].toWireType(destructors, arguments[i]);
|
||||
}
|
||||
var rv = returnType.fromWireType(invoker.apply(null, args));
|
||||
runDestructors(destructors);
|
||||
return rv;
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_tuple(tupleType, name, constructor, destructor) {
|
||||
name = Pointer_stringify(name);
|
||||
constructor = FUNCTION_TABLE[constructor];
|
||||
destructor = FUNCTION_TABLE[destructor];
|
||||
|
||||
var elements = [];
|
||||
|
||||
typeRegistry[tupleType] = {
|
||||
name: name,
|
||||
elements: elements,
|
||||
fromWireType: function(ptr) {
|
||||
var len = elements.length;
|
||||
var rv = new Array(len);
|
||||
for (var i = 0; i < len; ++i) {
|
||||
rv[i] = elements[i].read(ptr);
|
||||
}
|
||||
destructor(ptr);
|
||||
return rv;
|
||||
},
|
||||
toWireType: function(destructors, o) {
|
||||
var len = elements.length;
|
||||
if (len !== o.length) {
|
||||
throw new TypeError("Incorrect number of tuple elements");
|
||||
}
|
||||
var ptr = constructor();
|
||||
for (var i = 0; i < len; ++i) {
|
||||
elements[i].write(ptr, o[i]);
|
||||
}
|
||||
destructors.push(destructor);
|
||||
destructors.push(ptr);
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function copyMemberPointer(memberPointer, memberPointerSize) {
|
||||
var copy = _malloc(memberPointerSize);
|
||||
if (!copy) {
|
||||
throw new Error('Failed to allocate member pointer copy');
|
||||
}
|
||||
_memcpy(copy, memberPointer, memberPointerSize);
|
||||
return copy;
|
||||
}
|
||||
|
||||
function __embind_register_tuple_element(
|
||||
tupleType,
|
||||
elementType,
|
||||
getter,
|
||||
setter,
|
||||
memberPointerSize,
|
||||
memberPointer
|
||||
) {
|
||||
tupleType = requireRegisteredType(tupleType, 'tuple');
|
||||
elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]");
|
||||
getter = FUNCTION_TABLE[getter];
|
||||
setter = FUNCTION_TABLE[setter];
|
||||
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
|
||||
|
||||
tupleType.elements.push({
|
||||
read: function(ptr) {
|
||||
return elementType.fromWireType(getter(ptr, memberPointer));
|
||||
},
|
||||
write: function(ptr, o) {
|
||||
var destructors = [];
|
||||
setter(ptr, memberPointer, elementType.toWireType(destructors, o));
|
||||
runDestructors(destructors);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function __embind_register_tuple_element_accessor(
|
||||
tupleType,
|
||||
elementType,
|
||||
staticGetter,
|
||||
getterSize,
|
||||
getter,
|
||||
staticSetter,
|
||||
setterSize,
|
||||
setter
|
||||
) {
|
||||
tupleType = requireRegisteredType(tupleType, 'tuple');
|
||||
elementType = requireRegisteredType(elementType, "element " + tupleType.name + "[" + tupleType.elements.length + "]");
|
||||
staticGetter = FUNCTION_TABLE[staticGetter];
|
||||
getter = copyMemberPointer(getter, getterSize);
|
||||
staticSetter = FUNCTION_TABLE[staticSetter];
|
||||
setter = copyMemberPointer(setter, setterSize);
|
||||
|
||||
tupleType.elements.push({
|
||||
read: function(ptr) {
|
||||
return elementType.fromWireType(staticGetter(ptr, HEAP32[getter >> 2]));
|
||||
},
|
||||
write: function(ptr, o) {
|
||||
var destructors = [];
|
||||
staticSetter(
|
||||
ptr,
|
||||
HEAP32[setter >> 2],
|
||||
elementType.toWireType(destructors, o));
|
||||
runDestructors(destructors);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function __embind_register_struct(
|
||||
structType,
|
||||
name,
|
||||
constructor,
|
||||
destructor
|
||||
) {
|
||||
name = Pointer_stringify(name);
|
||||
constructor = FUNCTION_TABLE[constructor];
|
||||
destructor = FUNCTION_TABLE[destructor];
|
||||
|
||||
typeRegistry[structType] = {
|
||||
fields: {},
|
||||
fromWireType: function(ptr) {
|
||||
var fields = this.fields;
|
||||
var rv = {};
|
||||
for (var i in fields) {
|
||||
rv[i] = fields[i].read(ptr);
|
||||
}
|
||||
destructor(ptr);
|
||||
return rv;
|
||||
},
|
||||
toWireType: function(destructors, o) {
|
||||
var fields = this.fields;
|
||||
for (var fieldName in fields) {
|
||||
if (!(fieldName in o)) {
|
||||
throw new TypeError('Missing field');
|
||||
}
|
||||
}
|
||||
var ptr = constructor();
|
||||
for (var fieldName in fields) {
|
||||
fields[fieldName].write(ptr, o[fieldName]);
|
||||
}
|
||||
destructors.push(destructor);
|
||||
destructors.push(ptr);
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_struct_field(
|
||||
structType,
|
||||
fieldName,
|
||||
fieldType,
|
||||
getter,
|
||||
setter,
|
||||
memberPointerSize,
|
||||
memberPointer
|
||||
) {
|
||||
structType = requireRegisteredType(structType, 'struct');
|
||||
fieldName = Pointer_stringify(fieldName);
|
||||
fieldType = requireRegisteredType(fieldType, 'field "' + structType.name + '.' + fieldName + '"');
|
||||
getter = FUNCTION_TABLE[getter];
|
||||
setter = FUNCTION_TABLE[setter];
|
||||
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
|
||||
|
||||
structType.fields[fieldName] = {
|
||||
read: function(ptr) {
|
||||
return fieldType.fromWireType(getter(ptr, memberPointer));
|
||||
},
|
||||
write: function(ptr, o) {
|
||||
var destructors = [];
|
||||
setter(ptr, memberPointer, fieldType.toWireType(destructors, o));
|
||||
runDestructors(destructors);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_class(
|
||||
classType,
|
||||
name,
|
||||
destructor
|
||||
) {
|
||||
name = Pointer_stringify(name);
|
||||
destructor = FUNCTION_TABLE[destructor];
|
||||
|
||||
var Handle = createNamedFunction(name, function(ptr) {
|
||||
this.count = {value: 1};
|
||||
this.ptr = ptr;
|
||||
});
|
||||
|
||||
Handle.prototype.clone = function() {
|
||||
if (!this.ptr) {
|
||||
throw new BindingError(classType.name + ' instance already deleted');
|
||||
}
|
||||
|
||||
var clone = Object.create(Handle.prototype);
|
||||
clone.count = this.count;
|
||||
clone.ptr = this.ptr;
|
||||
|
||||
clone.count.value += 1;
|
||||
return clone;
|
||||
};
|
||||
|
||||
Handle.prototype.move = function() {
|
||||
var rv = this.clone();
|
||||
this.delete();
|
||||
return rv;
|
||||
};
|
||||
|
||||
Handle.prototype['delete'] = function() {
|
||||
if (!this.ptr) {
|
||||
throw new BindingError(classType.name + ' instance already deleted');
|
||||
}
|
||||
|
||||
this.count.value -= 1;
|
||||
if (0 === this.count.value) {
|
||||
destructor(this.ptr);
|
||||
}
|
||||
this.ptr = undefined;
|
||||
};
|
||||
|
||||
var constructor = createNamedFunction(name, function() {
|
||||
var body = constructor.body;
|
||||
body.apply(this, arguments);
|
||||
});
|
||||
constructor.prototype = Object.create(Handle.prototype);
|
||||
|
||||
typeRegistry[classType] = {
|
||||
name: name,
|
||||
constructor: constructor,
|
||||
Handle: Handle,
|
||||
fromWireType: function(ptr) {
|
||||
return new Handle(ptr);
|
||||
},
|
||||
toWireType: function(destructors, o) {
|
||||
return o.ptr;
|
||||
}
|
||||
};
|
||||
|
||||
Module[name] = constructor;
|
||||
}
|
||||
|
||||
function __embind_register_class_constructor(
|
||||
classType,
|
||||
argCount,
|
||||
argTypes,
|
||||
constructor
|
||||
) {
|
||||
classType = requireRegisteredType(classType, 'class');
|
||||
var humanName = 'constructor ' + classType.name;
|
||||
argTypes = requireArgumentTypes(argCount, argTypes, humanName);
|
||||
constructor = FUNCTION_TABLE[constructor];
|
||||
|
||||
classType.constructor.body = function() {
|
||||
if (arguments.length !== argCount) {
|
||||
throw new BindingError('emscripten binding ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount);
|
||||
}
|
||||
var destructors = [];
|
||||
var args = new Array(argCount);
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
args[i] = argTypes[i].toWireType(destructors, arguments[i]);
|
||||
}
|
||||
|
||||
var ptr = constructor.apply(null, args);
|
||||
runDestructors(destructors);
|
||||
classType.Handle.call(this, ptr);
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_class_method(
|
||||
classType,
|
||||
methodName,
|
||||
returnType,
|
||||
argCount,
|
||||
argTypes,
|
||||
invoker,
|
||||
memberFunctionSize,
|
||||
memberFunction
|
||||
) {
|
||||
classType = requireRegisteredType(classType, 'class');
|
||||
methodName = Pointer_stringify(methodName);
|
||||
var humanName = classType.name + '.' + methodName;
|
||||
returnType = requireRegisteredType(returnType, 'method ' + humanName + ' return value');
|
||||
argTypes = requireArgumentTypes(argCount, argTypes, 'method ' + humanName);
|
||||
invoker = FUNCTION_TABLE[invoker];
|
||||
memberFunction = copyMemberPointer(memberFunction, memberFunctionSize);
|
||||
|
||||
classType.Handle.prototype[methodName] = function() {
|
||||
if (!this.ptr) {
|
||||
throw new BindingError('cannot call emscripten binding method ' + humanName + ' on deleted object');
|
||||
}
|
||||
if (arguments.length !== argCount) {
|
||||
throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount);
|
||||
}
|
||||
|
||||
var destructors = [];
|
||||
var args = new Array(argCount + 2);
|
||||
args[0] = this.ptr;
|
||||
args[1] = memberFunction;
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
args[i + 2] = argTypes[i].toWireType(destructors, arguments[i]);
|
||||
}
|
||||
|
||||
var rv = returnType.fromWireType(invoker.apply(null, args));
|
||||
runDestructors(destructors);
|
||||
return rv;
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_class_classmethod(
|
||||
classType,
|
||||
methodName,
|
||||
returnType,
|
||||
argCount,
|
||||
argTypes,
|
||||
method
|
||||
) {
|
||||
classType = requireRegisteredType(classType, 'class');
|
||||
methodName = Pointer_stringify(methodName);
|
||||
var humanName = classType.name + '.' + methodName;
|
||||
returnType = requireRegisteredType(returnType, 'classmethod ' + humanName + ' return value');
|
||||
argTypes = requireArgumentTypes(argCount, argTypes, 'classmethod ' + humanName);
|
||||
method = FUNCTION_TABLE[method];
|
||||
|
||||
classType.constructor[methodName] = function() {
|
||||
if (arguments.length !== argCount) {
|
||||
throw new BindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + argCount);
|
||||
}
|
||||
|
||||
var destructors = [];
|
||||
var args = new Array(argCount);
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
args[i] = argTypes[i].toWireType(destructors, arguments[i]);
|
||||
}
|
||||
|
||||
var rv = returnType.fromWireType(method.apply(null, args));
|
||||
runDestructors(destructors);
|
||||
return rv;
|
||||
};
|
||||
}
|
||||
|
||||
function __embind_register_class_field(
|
||||
classType,
|
||||
fieldName,
|
||||
fieldType,
|
||||
getter,
|
||||
setter,
|
||||
memberPointerSize,
|
||||
memberPointer
|
||||
) {
|
||||
classType = requireRegisteredType(classType, 'class');
|
||||
fieldName = Pointer_stringify(fieldName);
|
||||
var humanName = classType.name + '.' + fieldName;
|
||||
fieldType = requireRegisteredType(fieldType, 'field ' + humanName);
|
||||
getter = FUNCTION_TABLE[getter];
|
||||
setter = FUNCTION_TABLE[setter];
|
||||
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
|
||||
|
||||
Object.defineProperty(classType.Handle.prototype, fieldName, {
|
||||
get: function() {
|
||||
if (!this.ptr) {
|
||||
throw new BindingError('cannot access emscripten binding field ' + humanName + ' on deleted object');
|
||||
}
|
||||
return fieldType.fromWireType(getter(this.ptr, memberPointer));
|
||||
},
|
||||
set: function(v) {
|
||||
if (!this.ptr) {
|
||||
throw new BindingError('cannot modify emscripten binding field ' + humanName + ' on deleted object');
|
||||
}
|
||||
var destructors = [];
|
||||
setter(this.ptr, memberPointer, fieldType.toWireType(destructors, v));
|
||||
runDestructors(destructors);
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
|
||||
function __embind_register_enum(
|
||||
enumType,
|
||||
name
|
||||
) {
|
||||
name = Pointer_stringify(name);
|
||||
|
||||
function Enum() {
|
||||
}
|
||||
Enum.values = {};
|
||||
|
||||
typeRegistry[enumType] = {
|
||||
name: name,
|
||||
constructor: Enum,
|
||||
toWireType: function(destructors, c) {
|
||||
return c.value;
|
||||
},
|
||||
fromWireType: function(c) {
|
||||
return Enum.values[c];
|
||||
},
|
||||
};
|
||||
|
||||
Module[name] = Enum;
|
||||
}
|
||||
|
||||
function __embind_register_enum_value(
|
||||
enumType,
|
||||
name,
|
||||
enumValue
|
||||
) {
|
||||
enumType = requireRegisteredType(enumType, 'enum');
|
||||
name = Pointer_stringify(name);
|
||||
|
||||
var Enum = enumType.constructor;
|
||||
|
||||
var Value = Object.create(enumType.constructor.prototype, {
|
||||
value: {value: enumValue},
|
||||
constructor: {value: createNamedFunction(enumType.name + '_' + name, function() {})},
|
||||
});
|
||||
Enum.values[enumValue] = Value;
|
||||
Enum[name] = Value;
|
||||
}
|
||||
|
||||
function __embind_register_interface(
|
||||
interfaceType,
|
||||
name,
|
||||
constructor,
|
||||
destructor
|
||||
) {
|
||||
name = Pointer_stringify(name);
|
||||
constructor = FUNCTION_TABLE[constructor];
|
||||
destructor = FUNCTION_TABLE[destructor];
|
||||
|
||||
typeRegistry[interfaceType] = {
|
||||
name: name,
|
||||
toWireType: function(destructors, o) {
|
||||
var handle = __emval_register(o);
|
||||
var ptr = constructor(handle);
|
||||
destructors.push(destructor);
|
||||
destructors.push(ptr);
|
||||
return ptr;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*global Module*/
|
||||
/*global HEAP32*/
|
||||
/*global Pointer_stringify, writeStringToMemory*/
|
||||
/*global requireRegisteredType*/
|
||||
|
||||
var _emval_handle_array = [];
|
||||
var _emval_free_list = [];
|
||||
|
||||
// Public JS API
|
||||
|
||||
/** @expose */
|
||||
Module.count_emval_handles = function() {
|
||||
return _emval_handle_array.length;
|
||||
};
|
||||
|
||||
// Private C++ API
|
||||
|
||||
function __emval_register(value) {
|
||||
var handle = _emval_free_list.length ?
|
||||
_emval_free_list.pop() :
|
||||
_emval_handle_array.length;
|
||||
_emval_handle_array[handle] = {refcount: 1, value: value};
|
||||
return handle;
|
||||
}
|
||||
|
||||
function __emval_incref(handle) {
|
||||
_emval_handle_array[handle].refcount += 1;
|
||||
}
|
||||
|
||||
function __emval_decref(handle) {
|
||||
if (0 === --_emval_handle_array[handle].refcount) {
|
||||
delete _emval_handle_array[handle];
|
||||
_emval_free_list.push(handle);
|
||||
|
||||
var actual_length = _emval_handle_array.length;
|
||||
while (actual_length > 0 && _emval_handle_array[actual_length - 1] === undefined) {
|
||||
--actual_length;
|
||||
}
|
||||
_emval_handle_array.length = actual_length;
|
||||
}
|
||||
}
|
||||
|
||||
function __emval_new_object() {
|
||||
return __emval_register({});
|
||||
}
|
||||
|
||||
function __emval_new_long(value) {
|
||||
return __emval_register(value);
|
||||
}
|
||||
|
||||
function __emval_new_cstring(str) {
|
||||
return __emval_register(Pointer_stringify(str));
|
||||
}
|
||||
|
||||
function __emval_get_property(handle, k) {
|
||||
k = Pointer_stringify(k);
|
||||
return __emval_register(_emval_handle_array[handle].value[k]);
|
||||
}
|
||||
|
||||
function __emval_get_property_by_long(handle, k) {
|
||||
return __emval_register(_emval_handle_array[handle].value[k]);
|
||||
}
|
||||
|
||||
function __emval_get_property_by_unsigned_long(handle, k) {
|
||||
return __emval_register(_emval_handle_array[handle].value[k]);
|
||||
}
|
||||
|
||||
function __emval_set_property(handle, k, value) {
|
||||
k = Pointer_stringify(k);
|
||||
_emval_handle_array[handle].value[k] = _emval_handle_array[value].value;
|
||||
}
|
||||
|
||||
function __emval_set_property_by_int(handle, k, value) {
|
||||
_emval_handle_array[handle].value[k] = _emval_handle_array[value].value;
|
||||
}
|
||||
|
||||
function __emval_as(handle, returnType) {
|
||||
returnType = requireRegisteredType(returnType, 'emval::as');
|
||||
var destructors = [];
|
||||
// caller owns destructing
|
||||
return returnType.toWireType(destructors, _emval_handle_array[handle].value);
|
||||
}
|
||||
|
||||
function __emval_call(handle, argCount, argTypes) {
|
||||
var args = Array.prototype.slice.call(arguments, 3);
|
||||
var fn = _emval_handle_array[handle].value;
|
||||
var a = new Array(argCount);
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
var argType = requireRegisteredType(
|
||||
HEAP32[(argTypes >> 2) + i],
|
||||
"parameter " + i);
|
||||
a[i] = argType.fromWireType(args[i]);
|
||||
}
|
||||
var rv = fn.apply(undefined, a);
|
||||
return __emval_register(rv);
|
||||
}
|
||||
|
||||
function __emval_call_method(handle, name, argCount, argTypes) {
|
||||
name = Pointer_stringify(name);
|
||||
var args = Array.prototype.slice.call(arguments, 4);
|
||||
var obj = _emval_handle_array[handle].value;
|
||||
var a = new Array(argCount);
|
||||
for (var i = 0; i < argCount; ++i) {
|
||||
var argType = requireRegisteredType(
|
||||
HEAP32[(argTypes >> 2) + i],
|
||||
"parameter " + i);
|
||||
a[i] = argType.fromWireType(args[i]);
|
||||
}
|
||||
var rv = obj[name].apply(obj, a);
|
||||
return __emval_register(rv);
|
||||
}
|
|
@ -0,0 +1,645 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <emscripten/val.h>
|
||||
#include <emscripten/wire.h>
|
||||
|
||||
namespace emscripten {
|
||||
namespace internal {
|
||||
typedef void (*GenericFunction)();
|
||||
typedef long GenericEnumValue;
|
||||
|
||||
// Implemented in JavaScript. Don't call these directly.
|
||||
extern "C" {
|
||||
void _embind_fatal_error(
|
||||
const char* name,
|
||||
const char* payload) __attribute__((noreturn));
|
||||
|
||||
void _embind_register_void(
|
||||
TypeID voidType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_bool(
|
||||
TypeID boolType,
|
||||
const char* name,
|
||||
bool trueValue,
|
||||
bool falseValue);
|
||||
|
||||
void _embind_register_integer(
|
||||
TypeID integerType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_float(
|
||||
TypeID floatType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_cstring(
|
||||
TypeID stringType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_emval(
|
||||
TypeID emvalType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_function(
|
||||
const char* name,
|
||||
TypeID returnType,
|
||||
unsigned argCount,
|
||||
TypeID argTypes[],
|
||||
GenericFunction invoker,
|
||||
GenericFunction function);
|
||||
|
||||
void _embind_register_tuple(
|
||||
TypeID tupleType,
|
||||
const char* name,
|
||||
GenericFunction constructor,
|
||||
GenericFunction destructor);
|
||||
|
||||
void _embind_register_tuple_element(
|
||||
TypeID tupleType,
|
||||
TypeID elementType,
|
||||
GenericFunction getter,
|
||||
GenericFunction setter,
|
||||
size_t memberPointerSize,
|
||||
void* memberPointer);
|
||||
|
||||
void _embind_register_tuple_element_accessor(
|
||||
TypeID tupleType,
|
||||
TypeID elementType,
|
||||
GenericFunction staticGetter,
|
||||
size_t getterSize,
|
||||
void* getter,
|
||||
GenericFunction staticSetter,
|
||||
size_t setterSize,
|
||||
void* setter);
|
||||
|
||||
void _embind_register_struct(
|
||||
TypeID structType,
|
||||
const char* name,
|
||||
GenericFunction constructor,
|
||||
GenericFunction destructor);
|
||||
|
||||
void _embind_register_struct_field(
|
||||
TypeID structType,
|
||||
const char* name,
|
||||
TypeID fieldType,
|
||||
GenericFunction getter,
|
||||
GenericFunction setter,
|
||||
size_t memberPointerSize,
|
||||
void* memberPointer);
|
||||
|
||||
void _embind_register_class(
|
||||
TypeID classType,
|
||||
const char* className,
|
||||
GenericFunction destructor);
|
||||
|
||||
void _embind_register_class_constructor(
|
||||
TypeID classType,
|
||||
unsigned argCount,
|
||||
TypeID argTypes[],
|
||||
GenericFunction constructor);
|
||||
|
||||
void _embind_register_class_method(
|
||||
TypeID classType,
|
||||
const char* methodName,
|
||||
TypeID returnType,
|
||||
unsigned argCount,
|
||||
TypeID argTypes[],
|
||||
GenericFunction invoker,
|
||||
size_t memberFunctionSize,
|
||||
void* memberFunction);
|
||||
|
||||
void _embind_register_class_field(
|
||||
TypeID classType,
|
||||
const char* fieldName,
|
||||
TypeID fieldType,
|
||||
GenericFunction getter,
|
||||
GenericFunction setter,
|
||||
size_t memberPointerSize,
|
||||
void* memberPointer);
|
||||
|
||||
void _embind_register_class_classmethod(
|
||||
TypeID classType,
|
||||
const char* methodName,
|
||||
TypeID returnType,
|
||||
unsigned argCount,
|
||||
TypeID argTypes[],
|
||||
GenericFunction method);
|
||||
|
||||
void _embind_register_enum(
|
||||
TypeID enumType,
|
||||
const char* name);
|
||||
|
||||
void _embind_register_enum_value(
|
||||
TypeID enumType,
|
||||
const char* valueName,
|
||||
GenericEnumValue value);
|
||||
|
||||
void _embind_register_interface(
|
||||
TypeID interfaceType,
|
||||
const char* name,
|
||||
GenericFunction constructor,
|
||||
GenericFunction destructor);
|
||||
}
|
||||
|
||||
extern void registerStandardTypes();
|
||||
|
||||
class BindingsDefinition {
|
||||
public:
|
||||
template<typename Function>
|
||||
BindingsDefinition(Function fn) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace emscripten {
|
||||
namespace internal {
|
||||
template<typename ReturnType, typename... Args>
|
||||
struct Invoker {
|
||||
static typename internal::BindingType<ReturnType>::WireType invoke(
|
||||
ReturnType (fn)(Args...),
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return internal::BindingType<ReturnType>::toWireType(
|
||||
fn(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
struct Invoker<void, Args...> {
|
||||
static void invoke(
|
||||
void (fn)(Args...),
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return fn(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ReturnType, typename... Args>
|
||||
void function(const char* name, ReturnType (fn)(Args...)) {
|
||||
internal::registerStandardTypes();
|
||||
|
||||
internal::ArgTypeList<Args...> args;
|
||||
internal::_embind_register_function(
|
||||
name,
|
||||
internal::getTypeID<ReturnType>(),
|
||||
args.count,
|
||||
args.types,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::Invoker<ReturnType, Args...>::invoke),
|
||||
reinterpret_cast<internal::GenericFunction>(fn));
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
template<typename ClassType, typename... Args>
|
||||
ClassType* raw_constructor(
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return new ClassType(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename ClassType>
|
||||
void raw_destructor(ClassType* ptr) {
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
template<typename ClassType, typename ReturnType, typename... Args>
|
||||
struct MethodInvoker {
|
||||
typedef ReturnType (ClassType::*MemberPointer)(Args...);
|
||||
typename internal::BindingType<ReturnType>::WireType invoke(
|
||||
ClassType* ptr,
|
||||
const MemberPointer& method,
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return internal::BindingType<ReturnType>::toWireType(
|
||||
(ptr->*method)(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ClassType, typename... Args>
|
||||
struct MethodInvoker<ClassType, void, Args...> {
|
||||
typedef void (ClassType::*MemberPointer)(Args...);
|
||||
static void invoke(
|
||||
ClassType* ptr,
|
||||
const MemberPointer& method,
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return (ptr->*method)(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ClassType, typename ReturnType, typename... Args>
|
||||
struct ConstMethodInvoker {
|
||||
typedef ReturnType (ClassType::*MemberPointer)(Args...) const;
|
||||
static typename internal::BindingType<ReturnType>::WireType invoke(
|
||||
const ClassType* ptr,
|
||||
const MemberPointer& method,
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return internal::BindingType<ReturnType>::toWireType(
|
||||
(ptr->*method)(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ClassType, typename... Args>
|
||||
struct ConstMethodInvoker<ClassType, void, Args...> {
|
||||
typedef void (ClassType::*MemberPointer)(Args...) const;
|
||||
static void invoke(
|
||||
const ClassType* ptr,
|
||||
const MemberPointer& method,
|
||||
typename internal::BindingType<Args>::WireType... args
|
||||
) {
|
||||
return (ptr->*method)(
|
||||
internal::BindingType<Args>::fromWireType(args)...
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ClassType, typename FieldType>
|
||||
struct FieldAccess {
|
||||
typedef FieldType ClassType::*MemberPointer;
|
||||
typedef internal::BindingType<FieldType> FieldBinding;
|
||||
typedef typename FieldBinding::WireType WireType;
|
||||
|
||||
static WireType get(
|
||||
ClassType& ptr,
|
||||
const MemberPointer& field
|
||||
) {
|
||||
return FieldBinding::toWireType(ptr.*field);
|
||||
}
|
||||
|
||||
static void set(
|
||||
ClassType& ptr,
|
||||
const MemberPointer& field,
|
||||
WireType value
|
||||
) {
|
||||
ptr.*field = FieldBinding::fromWireType(value);
|
||||
}
|
||||
|
||||
template<typename Getter>
|
||||
static WireType propertyGet(
|
||||
ClassType& ptr,
|
||||
const Getter& getter
|
||||
) {
|
||||
return FieldBinding::toWireType(getter(ptr));
|
||||
}
|
||||
|
||||
template<typename Setter>
|
||||
static void propertySet(
|
||||
ClassType& ptr,
|
||||
const Setter& setter,
|
||||
WireType value
|
||||
) {
|
||||
setter(ptr, FieldBinding::fromWireType(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ClassType>
|
||||
class value_tuple {
|
||||
public:
|
||||
value_tuple(const char* name) {
|
||||
internal::registerStandardTypes();
|
||||
internal::_embind_register_tuple(
|
||||
internal::getTypeID<ClassType>(),
|
||||
name,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
value_tuple& element(ElementType ClassType::*field) {
|
||||
internal::_embind_register_tuple_element(
|
||||
internal::getTypeID<ClassType>(),
|
||||
internal::getTypeID<ElementType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::get),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::set),
|
||||
sizeof(field),
|
||||
&field);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType)) {
|
||||
internal::_embind_register_tuple_element_accessor(
|
||||
internal::getTypeID<ClassType>(),
|
||||
internal::getTypeID<ElementType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
|
||||
sizeof(getter),
|
||||
&getter,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
|
||||
sizeof(setter),
|
||||
&setter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&)) {
|
||||
internal::_embind_register_tuple_element_accessor(
|
||||
internal::getTypeID<ClassType>(),
|
||||
internal::getTypeID<ElementType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
|
||||
sizeof(getter),
|
||||
&getter,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
|
||||
sizeof(setter),
|
||||
&setter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, const ElementType&&)) {
|
||||
internal::_embind_register_tuple_element_accessor(
|
||||
internal::getTypeID<ClassType>(),
|
||||
internal::getTypeID<ElementType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
|
||||
sizeof(getter),
|
||||
&getter,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
|
||||
sizeof(setter),
|
||||
&setter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
value_tuple& element(ElementType (*getter)(const ClassType&), void (*setter)(ClassType&, ElementType&)) {
|
||||
internal::_embind_register_tuple_element_accessor(
|
||||
internal::getTypeID<ClassType>(),
|
||||
internal::getTypeID<ElementType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertyGet<ElementType(const ClassType&)>),
|
||||
sizeof(getter),
|
||||
&getter,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, ElementType>::template propertySet<void(ClassType&, ElementType)>),
|
||||
sizeof(setter),
|
||||
&setter);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ClassType>
|
||||
class value_struct {
|
||||
public:
|
||||
value_struct(const char* name) {
|
||||
internal::registerStandardTypes();
|
||||
internal::_embind_register_struct(
|
||||
internal::getTypeID<ClassType>(),
|
||||
name,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType>),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
value_struct& field(const char* fieldName, FieldType ClassType::*field) {
|
||||
internal::_embind_register_struct_field(
|
||||
internal::getTypeID<ClassType>(),
|
||||
fieldName,
|
||||
internal::getTypeID<FieldType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set),
|
||||
sizeof(field),
|
||||
&field);
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: support class definitions without constructors.
|
||||
// TODO: support external class constructors
|
||||
template<typename ClassType>
|
||||
class class_ {
|
||||
public:
|
||||
class_(const char* name) {
|
||||
internal::registerStandardTypes();
|
||||
internal::_embind_register_class(
|
||||
internal::getTypeID<ClassType>(),
|
||||
name,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<ClassType>));
|
||||
}
|
||||
|
||||
template<typename... ConstructorArgs>
|
||||
class_& constructor() {
|
||||
internal::ArgTypeList<ConstructorArgs...> args;
|
||||
internal::_embind_register_class_constructor(
|
||||
internal::getTypeID<ClassType>(),
|
||||
args.count,
|
||||
args.types,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_constructor<ClassType, ConstructorArgs...>));
|
||||
}
|
||||
|
||||
template<typename ReturnType, typename... Args>
|
||||
class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...)) {
|
||||
internal::ArgTypeList<Args...> args;
|
||||
internal::_embind_register_class_method(
|
||||
internal::getTypeID<ClassType>(),
|
||||
methodName,
|
||||
internal::getTypeID<ReturnType>(),
|
||||
args.count,
|
||||
args.types,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::MethodInvoker<ClassType, ReturnType, Args...>::invoke),
|
||||
sizeof(memberFunction),
|
||||
&memberFunction);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ReturnType, typename... Args>
|
||||
class_& method(const char* methodName, ReturnType (ClassType::*memberFunction)(Args...) const) {
|
||||
internal::ArgTypeList<Args...> args;
|
||||
internal::_embind_register_class_method(
|
||||
internal::getTypeID<ClassType>(),
|
||||
methodName,
|
||||
internal::getTypeID<ReturnType>(),
|
||||
args.count,
|
||||
args.types,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::ConstMethodInvoker<ClassType, ReturnType, Args...>::invoke),
|
||||
sizeof(memberFunction),
|
||||
&memberFunction);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename FieldType>
|
||||
class_& field(const char* fieldName, FieldType ClassType::*field) {
|
||||
internal::_embind_register_class_field(
|
||||
internal::getTypeID<ClassType>(),
|
||||
fieldName,
|
||||
internal::getTypeID<FieldType>(),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::get),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::FieldAccess<ClassType, FieldType>::set),
|
||||
sizeof(field),
|
||||
&field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename ReturnType, typename... Args>
|
||||
class_& classmethod(const char* methodName, ReturnType (*classMethod)(Args...)) {
|
||||
internal::ArgTypeList<Args...> args;
|
||||
internal::_embind_register_class_classmethod(
|
||||
internal::getTypeID<ClassType>(),
|
||||
methodName,
|
||||
internal::getTypeID<ReturnType>(),
|
||||
args.count,
|
||||
args.types,
|
||||
reinterpret_cast<internal::GenericFunction>(classMethod));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename EnumType>
|
||||
class enum_ {
|
||||
public:
|
||||
enum_(const char* name) {
|
||||
_embind_register_enum(
|
||||
internal::getTypeID<EnumType>(),
|
||||
name);
|
||||
}
|
||||
|
||||
enum_& value(const char* name, EnumType value) {
|
||||
// TODO: there's still an issue here.
|
||||
// if EnumType is an unsigned long, then JS may receive it as a signed long
|
||||
static_assert(sizeof(value) <= sizeof(internal::GenericEnumValue), "enum type must fit in a GenericEnumValue");
|
||||
|
||||
_embind_register_enum_value(
|
||||
internal::getTypeID<EnumType>(),
|
||||
name,
|
||||
static_cast<internal::GenericEnumValue>(value));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
template<typename T>
|
||||
class optional {
|
||||
public:
|
||||
optional()
|
||||
: initialized(false)
|
||||
{}
|
||||
|
||||
~optional() {
|
||||
if (initialized) {
|
||||
get()->~T();
|
||||
}
|
||||
}
|
||||
|
||||
optional(const optional&) = delete;
|
||||
|
||||
T& operator*() {
|
||||
assert(initialized);
|
||||
return *get();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
optional& operator=(const T& v) {
|
||||
if (initialized) {
|
||||
get()->~T();
|
||||
}
|
||||
new(get()) T(v);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private:
|
||||
T* get() {
|
||||
return reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
typename std::aligned_storage<sizeof(T)>::type data;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename InterfaceType>
|
||||
class wrapper : public InterfaceType {
|
||||
public:
|
||||
// Not necessary in any example so far, but appeases a compiler warning.
|
||||
virtual ~wrapper() {}
|
||||
|
||||
typedef InterfaceType interface;
|
||||
|
||||
void initialize(internal::EM_VAL handle) {
|
||||
if (jsobj) {
|
||||
internal::_embind_fatal_error(
|
||||
"Cannot initialize interface wrapper twice",
|
||||
typeid(InterfaceType).name());
|
||||
}
|
||||
jsobj = val::take_ownership(handle);
|
||||
}
|
||||
|
||||
template<typename ReturnType, typename... Args>
|
||||
ReturnType call(const char* name, Args... args) {
|
||||
assertInitialized();
|
||||
return Caller<ReturnType, Args...>::call(*jsobj, name, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
// this class only exists because you can't partially specialize function templates
|
||||
template<typename ReturnType, typename... Args>
|
||||
struct Caller {
|
||||
static ReturnType call(val& v, const char* name, Args... args) {
|
||||
return v.call(name, args...).template as<ReturnType>();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
struct Caller<void, Args...> {
|
||||
static void call(val& v, const char* name, Args... args) {
|
||||
v.call(name, args...);
|
||||
}
|
||||
};
|
||||
|
||||
void assertInitialized() {
|
||||
if (!jsobj) {
|
||||
internal::_embind_fatal_error(
|
||||
"Cannot invoke call on uninitialized interface wrapper.",
|
||||
typeid(InterfaceType).name());
|
||||
}
|
||||
}
|
||||
|
||||
internal::optional<val> jsobj;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
template<typename WrapperType>
|
||||
WrapperType* create_interface_wrapper(EM_VAL e) {
|
||||
WrapperType* p = new WrapperType;
|
||||
p->initialize(e);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename WrapperType>
|
||||
class interface {
|
||||
public:
|
||||
typedef typename WrapperType::interface InterfaceType;
|
||||
|
||||
interface(const char* name) {
|
||||
_embind_register_interface(
|
||||
internal::getTypeID<InterfaceType>(),
|
||||
name,
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::create_interface_wrapper<WrapperType>),
|
||||
reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<WrapperType>));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define EMSCRIPTEN_BINDINGS(fn) static emscripten::internal::BindingsDefinition anon_symbol(fn);
|
|
@ -0,0 +1,177 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h> // uintptr_t
|
||||
#include <emscripten/wire.h>
|
||||
|
||||
namespace emscripten {
|
||||
namespace internal {
|
||||
// Implemented in JavaScript. Don't call these directly.
|
||||
extern "C" {
|
||||
typedef struct _EM_VAL* EM_VAL;
|
||||
|
||||
void _emval_incref(EM_VAL value);
|
||||
void _emval_decref(EM_VAL value);
|
||||
EM_VAL _emval_new_object();
|
||||
EM_VAL _emval_new_long(long value);
|
||||
EM_VAL _emval_new_cstring(const char* str);
|
||||
EM_VAL _emval_get_property(EM_VAL object, const char* key);
|
||||
EM_VAL _emval_get_property_by_long(EM_VAL object, long key);
|
||||
EM_VAL _emval_get_property_by_unsigned_long(EM_VAL object, unsigned long key);
|
||||
void _emval_set_property(EM_VAL object, const char* key, EM_VAL value);
|
||||
void _emval_set_property_by_int(EM_VAL object, long key, EM_VAL value);
|
||||
void _emval_as(EM_VAL value, emscripten::internal::TypeID returnType);
|
||||
EM_VAL _emval_call(
|
||||
EM_VAL value,
|
||||
unsigned argCount,
|
||||
internal::TypeID argTypes[]
|
||||
/*, ... */);
|
||||
EM_VAL _emval_call_method(
|
||||
EM_VAL value,
|
||||
const char* methodName,
|
||||
unsigned argCount,
|
||||
internal::TypeID argTypes[]
|
||||
/*, ... */);
|
||||
}
|
||||
}
|
||||
|
||||
class val {
|
||||
public:
|
||||
static val object() {
|
||||
return val(internal::_emval_new_object());
|
||||
};
|
||||
|
||||
static val take_ownership(internal::EM_VAL e) {
|
||||
return val(e);
|
||||
}
|
||||
|
||||
explicit val(long l)
|
||||
: handle(internal::_emval_new_long(l))
|
||||
{}
|
||||
|
||||
explicit val(const char* str)
|
||||
: handle(internal::_emval_new_cstring(str))
|
||||
{}
|
||||
|
||||
val() = delete;
|
||||
|
||||
val(const val& v)
|
||||
: handle(v.handle)
|
||||
{
|
||||
internal::_emval_incref(handle);
|
||||
}
|
||||
|
||||
~val() {
|
||||
internal::_emval_decref(handle);
|
||||
}
|
||||
|
||||
val& operator=(const val& v) {
|
||||
internal::_emval_incref(v.handle);
|
||||
internal::_emval_decref(handle);
|
||||
handle = v.handle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
val get(const char* key) const {
|
||||
return val(internal::_emval_get_property(handle, key));
|
||||
}
|
||||
|
||||
val get(int key) const {
|
||||
return get(long(key));
|
||||
}
|
||||
|
||||
val get(unsigned int key) const {
|
||||
typedef unsigned long T;
|
||||
return get(T(key));
|
||||
}
|
||||
|
||||
val get(long key) const {
|
||||
return val(internal::_emval_get_property_by_long(handle, key));
|
||||
}
|
||||
|
||||
val get(unsigned long key) const {
|
||||
return val(internal::_emval_get_property_by_unsigned_long(handle, key));
|
||||
}
|
||||
|
||||
void set(const char* key, val v) {
|
||||
internal::_emval_set_property(handle, key, v.handle);
|
||||
}
|
||||
|
||||
void set(long key, val v) {
|
||||
internal::_emval_set_property_by_int(handle, key, v.handle);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
val operator()(Args... args) {
|
||||
internal::ArgTypeList<Args...> argList;
|
||||
typedef internal::EM_VAL (*TypedCall)(
|
||||
internal::EM_VAL,
|
||||
unsigned,
|
||||
internal::TypeID argTypes[],
|
||||
typename internal::BindingType<Args>::WireType...);
|
||||
TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call);
|
||||
return val(
|
||||
typedCall(
|
||||
handle,
|
||||
argList.count,
|
||||
argList.types,
|
||||
internal::toWireType(args)...));
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
val call(const char* name, Args... args) {
|
||||
internal::ArgTypeList<Args...> argList;
|
||||
typedef internal::EM_VAL (*TypedCall)(
|
||||
internal::EM_VAL,
|
||||
const char* name,
|
||||
unsigned,
|
||||
internal::TypeID argTypes[],
|
||||
typename internal::BindingType<Args>::WireType...);
|
||||
TypedCall typedCall = reinterpret_cast<TypedCall>(&internal::_emval_call_method);
|
||||
return val(
|
||||
typedCall(
|
||||
handle,
|
||||
name,
|
||||
argList.count,
|
||||
argList.types,
|
||||
internal::toWireType(args)...));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T as() const {
|
||||
typedef internal::BindingType<T> BT;
|
||||
|
||||
typedef typename BT::WireType (*TypedAs)(
|
||||
internal::EM_VAL value,
|
||||
emscripten::internal::TypeID returnType);
|
||||
TypedAs typedAs = reinterpret_cast<TypedAs>(&internal::_emval_as);
|
||||
|
||||
typename BT::WireType wt = typedAs(handle, internal::getTypeID<T>());
|
||||
internal::WireDeleter<T> deleter(wt);
|
||||
return BT::fromWireType(wt);
|
||||
}
|
||||
|
||||
private:
|
||||
// takes ownership, assumes handle already incref'd
|
||||
explicit val(internal::EM_VAL handle)
|
||||
: handle(handle)
|
||||
{}
|
||||
|
||||
internal::EM_VAL handle;
|
||||
|
||||
friend struct internal::BindingType<val>;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
template<>
|
||||
struct BindingType<val> {
|
||||
typedef internal::EM_VAL WireType;
|
||||
static WireType toWireType(val v) {
|
||||
_emval_incref(v.handle);
|
||||
return v.handle;
|
||||
}
|
||||
static val fromWireType(WireType v) {
|
||||
return val(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
#pragma once
|
||||
|
||||
// A value moving between JavaScript and C++ has three representations:
|
||||
// - The original JS value: a String
|
||||
// - The native on-the-wire value: a stack-allocated char*, say
|
||||
// - The C++ value: std::string
|
||||
//
|
||||
// We'll call the on-the-wire type WireType.
|
||||
|
||||
namespace emscripten {
|
||||
namespace internal {
|
||||
typedef const struct _TypeID* TypeID;
|
||||
|
||||
// This implementation is technically not legal, as it's not
|
||||
// required that two calls to typeid produce the same exact
|
||||
// std::type_info instance. That said, it's likely to work.
|
||||
// Should it not work in the future: replace TypeID with
|
||||
// an int, and store all TypeInfo we see in a map, allocating
|
||||
// new TypeIDs as we add new items to the map.
|
||||
template<typename T>
|
||||
inline TypeID getTypeID() {
|
||||
return reinterpret_cast<TypeID>(&typeid(T));
|
||||
}
|
||||
|
||||
// count<>
|
||||
|
||||
template<typename... Args>
|
||||
struct count;
|
||||
|
||||
template<>
|
||||
struct count<> {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct count<T, Args...> {
|
||||
enum { value = 1 + count<Args...>::value };
|
||||
};
|
||||
|
||||
// ArgTypeList<>
|
||||
|
||||
template<typename... Args>
|
||||
struct ArgTypes;
|
||||
|
||||
template<>
|
||||
struct ArgTypes<> {
|
||||
static void fill(TypeID* argTypes) {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
struct ArgTypes<T, Args...> {
|
||||
static void fill(TypeID* argTypes) {
|
||||
*argTypes = getTypeID<T>();
|
||||
return ArgTypes<Args...>::fill(argTypes + 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
struct ArgTypeList {
|
||||
enum { args_count = count<Args...>::value };
|
||||
|
||||
ArgTypeList() {
|
||||
count = args_count;
|
||||
ArgTypes<Args...>::fill(types);
|
||||
}
|
||||
|
||||
unsigned count;
|
||||
TypeID types[args_count];
|
||||
};
|
||||
|
||||
// BindingType<T>
|
||||
|
||||
template<typename T>
|
||||
struct BindingType;
|
||||
|
||||
#define EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(type) \
|
||||
template<> \
|
||||
struct BindingType<type> { \
|
||||
typedef type WireType; \
|
||||
\
|
||||
constexpr static WireType toWireType(type v) { \
|
||||
return v; \
|
||||
} \
|
||||
constexpr static type fromWireType(WireType v) { \
|
||||
return v; \
|
||||
} \
|
||||
static void destroy(WireType) { \
|
||||
} \
|
||||
}
|
||||
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(char);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed char);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned char);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed short);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned short);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed int);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned int);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(signed long);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(unsigned long);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(float);
|
||||
EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(double);
|
||||
|
||||
template<>
|
||||
struct BindingType<void> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BindingType<bool> {
|
||||
typedef bool WireType;
|
||||
static WireType toWireType(bool b) {
|
||||
return b;
|
||||
}
|
||||
static bool fromWireType(WireType wt) {
|
||||
return wt;
|
||||
}
|
||||
static void destroy(WireType) {
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BindingType<std::string> {
|
||||
typedef char* WireType;
|
||||
static WireType toWireType(std::string v) {
|
||||
return strdup(v.c_str());
|
||||
}
|
||||
static std::string fromWireType(char* v) {
|
||||
return std::string(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BindingType<const std::string&> {
|
||||
typedef char* WireType;
|
||||
static WireType toWireType(std::string v) {
|
||||
return strdup(v.c_str());
|
||||
}
|
||||
static std::string fromWireType(char* v) {
|
||||
return std::string(v);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Enum>
|
||||
struct EnumBindingType {
|
||||
typedef Enum WireType;
|
||||
|
||||
static WireType toWireType(Enum v) {
|
||||
return v;
|
||||
}
|
||||
static Enum fromWireType(WireType v) {
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct GenericBindingType {
|
||||
typedef typename std::remove_reference<T>::type ActualT;
|
||||
typedef ActualT* WireType;
|
||||
|
||||
struct Marshaller {
|
||||
explicit Marshaller(WireType wt)
|
||||
: wireType(wt)
|
||||
{}
|
||||
|
||||
Marshaller(Marshaller&& wt)
|
||||
: wireType(wt.wireType)
|
||||
{
|
||||
wt.wireType = 0;
|
||||
}
|
||||
|
||||
operator ActualT&() const {
|
||||
return *wireType;
|
||||
}
|
||||
|
||||
private:
|
||||
Marshaller() = delete;
|
||||
Marshaller(const Marshaller&) = delete;
|
||||
ActualT* wireType;
|
||||
};
|
||||
|
||||
static WireType toWireType(T v) {
|
||||
return new T(v);
|
||||
}
|
||||
|
||||
static Marshaller fromWireType(WireType p) {
|
||||
return Marshaller(p);
|
||||
}
|
||||
|
||||
static void destroy(WireType p) {
|
||||
delete p;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct WireDeleter {
|
||||
typedef typename BindingType<T>::WireType WireType;
|
||||
|
||||
WireDeleter(WireType wt)
|
||||
: wt(wt)
|
||||
{}
|
||||
|
||||
~WireDeleter() {
|
||||
BindingType<T>::destroy(wt);
|
||||
}
|
||||
|
||||
WireType wt;
|
||||
};
|
||||
|
||||
// catch-all generic binding
|
||||
template<typename T>
|
||||
struct BindingType : std::conditional<
|
||||
std::is_enum<T>::value,
|
||||
EnumBindingType<T>,
|
||||
GenericBindingType<T>>::type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
auto toWireType(const T& v) -> typename BindingType<T>::WireType {
|
||||
return BindingType<T>::toWireType(v);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#include <emscripten/bind.h>
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
namespace emscripten {
|
||||
namespace internal {
|
||||
void registerStandardTypes() {
|
||||
static bool first = true;
|
||||
if (first) {
|
||||
first = false;
|
||||
|
||||
_embind_register_void(getTypeID<void>(), "void");
|
||||
|
||||
_embind_register_bool(getTypeID<bool>(), "bool", true, false);
|
||||
|
||||
_embind_register_integer(getTypeID<char>(), "char");
|
||||
_embind_register_integer(getTypeID<signed char>(), "signed char");
|
||||
_embind_register_integer(getTypeID<unsigned char>(), "unsigned char");
|
||||
_embind_register_integer(getTypeID<signed short>(), "short");
|
||||
_embind_register_integer(getTypeID<unsigned short>(), "unsigned short");
|
||||
_embind_register_integer(getTypeID<signed int>(), "int");
|
||||
_embind_register_integer(getTypeID<unsigned int>(), "unsigned int");
|
||||
_embind_register_integer(getTypeID<signed long>(), "long");
|
||||
_embind_register_integer(getTypeID<unsigned long>(), "unsigned long");
|
||||
|
||||
_embind_register_float(getTypeID<float>(), "float");
|
||||
_embind_register_float(getTypeID<double>(), "double");
|
||||
|
||||
_embind_register_cstring(getTypeID<std::string>(), "std::string");
|
||||
_embind_register_emval(getTypeID<val>(), "emscripten::val");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
#include <string>
|
||||
#include <malloc.h>
|
||||
#include <emscripten/bind.h>
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
val emval_test_mallinfo() {
|
||||
const auto& i = mallinfo();
|
||||
val rv(val::object());
|
||||
rv.set("arena", val(i.arena));
|
||||
rv.set("ordblks", val(i.ordblks));
|
||||
rv.set("smblks", val(i.smblks));
|
||||
rv.set("hblks", val(i.hblks));
|
||||
rv.set("usmblks", val(i.usmblks));
|
||||
rv.set("fsmblks", val(i.fsmblks));
|
||||
rv.set("uordblks", val(i.uordblks));
|
||||
rv.set("fordblks", val(i.fordblks));
|
||||
rv.set("keepcost", val(i.keepcost));
|
||||
return rv;
|
||||
}
|
||||
|
||||
val emval_test_new_integer() {
|
||||
return val(15);
|
||||
}
|
||||
|
||||
val emval_test_new_string() {
|
||||
return val("Hello everyone");
|
||||
}
|
||||
|
||||
val emval_test_new_object() {
|
||||
val rv(val::object());
|
||||
rv.set("foo", val("bar"));
|
||||
rv.set("baz", val(1));
|
||||
return rv;
|
||||
}
|
||||
|
||||
unsigned emval_test_passthrough_unsigned(unsigned v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
val emval_test_passthrough(val v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
void emval_test_return_void() {
|
||||
}
|
||||
|
||||
bool emval_test_not(bool b) {
|
||||
return !b;
|
||||
}
|
||||
|
||||
unsigned emval_test_as_unsigned(val v) {
|
||||
return v.as<unsigned>();
|
||||
}
|
||||
|
||||
unsigned emval_test_get_length(val v) {
|
||||
return v.get("length").as<unsigned>();
|
||||
}
|
||||
|
||||
double emval_test_add(char c, signed char sc, unsigned char uc, signed short ss, unsigned short us, signed int si, unsigned int ui, signed long sl, unsigned long ul, float f, double d) {
|
||||
return c + sc + uc + ss + us + si + ui + sl + ul + f + d;
|
||||
}
|
||||
|
||||
unsigned emval_test_sum(val v) {
|
||||
unsigned length = v.get("length").as<unsigned>();
|
||||
double rv = 0;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
rv += v.get(i).as<double>();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::string emval_test_take_and_return_const_char_star(const char* str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string emval_test_take_and_return_std_string(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string emval_test_take_and_return_std_string_const_ref(const std::string& str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
class ValHolder {
|
||||
public:
|
||||
ValHolder(val v)
|
||||
: v(v)
|
||||
{}
|
||||
|
||||
val getVal() const {
|
||||
return v;
|
||||
}
|
||||
|
||||
void setVal(val v) {
|
||||
this->v = v;
|
||||
}
|
||||
|
||||
static int some_class_method(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
private:
|
||||
val v;
|
||||
};
|
||||
|
||||
ValHolder emval_test_return_ValHolder() {
|
||||
return val::object();
|
||||
}
|
||||
|
||||
void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) {
|
||||
vh.setVal(val::object());
|
||||
}
|
||||
|
||||
class StringHolder {
|
||||
public:
|
||||
StringHolder(const std::string& s)
|
||||
: str(s)
|
||||
{}
|
||||
|
||||
void set(const std::string& s) {
|
||||
str = s;
|
||||
}
|
||||
std::string get() const {
|
||||
return str;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str;
|
||||
};
|
||||
|
||||
struct TupleVector {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
float readTupleVectorZ(const TupleVector& v) {
|
||||
return v.z;
|
||||
}
|
||||
|
||||
void writeTupleVectorZ(TupleVector& v, float z) {
|
||||
v.z = z;
|
||||
}
|
||||
|
||||
struct TupleVectorTuple {
|
||||
TupleVector v;
|
||||
};
|
||||
|
||||
TupleVector emval_test_return_TupleVector() {
|
||||
TupleVector cv;
|
||||
cv.x = 1;
|
||||
cv.y = 2;
|
||||
cv.z = 3;
|
||||
return cv;
|
||||
}
|
||||
|
||||
TupleVector emval_test_take_and_return_TupleVector(TupleVector v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
TupleVectorTuple emval_test_return_TupleVectorTuple() {
|
||||
TupleVectorTuple cvt;
|
||||
cvt.v = emval_test_return_TupleVector();
|
||||
return cvt;
|
||||
}
|
||||
|
||||
struct StructVector {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
StructVector emval_test_return_StructVector() {
|
||||
StructVector v;
|
||||
v.x = 1;
|
||||
v.y = 2;
|
||||
v.z = 3;
|
||||
return v;
|
||||
}
|
||||
|
||||
StructVector emval_test_take_and_return_StructVector(StructVector v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
struct CustomStruct {
|
||||
CustomStruct()
|
||||
: field(10)
|
||||
{}
|
||||
int field;
|
||||
};
|
||||
|
||||
struct TupleInStruct {
|
||||
TupleVector field;
|
||||
};
|
||||
|
||||
TupleInStruct emval_test_take_and_return_TupleInStruct(TupleInStruct cs) {
|
||||
return cs;
|
||||
}
|
||||
|
||||
enum Enum { ONE, TWO };
|
||||
|
||||
Enum emval_test_take_and_return_Enum(Enum e) {
|
||||
return e;
|
||||
}
|
||||
|
||||
enum class EnumClass { ONE, TWO };
|
||||
|
||||
EnumClass emval_test_take_and_return_EnumClass(EnumClass e) {
|
||||
return e;
|
||||
}
|
||||
|
||||
class Interface {
|
||||
public:
|
||||
virtual int method() = 0;
|
||||
virtual TupleInStruct method2(const TupleInStruct& arg1, float arg2) = 0;
|
||||
virtual void method3() = 0;
|
||||
};
|
||||
|
||||
int emval_test_call_method(Interface& i) {
|
||||
return i.method();
|
||||
}
|
||||
|
||||
TupleInStruct emval_test_call_method2(Interface& i, const TupleInStruct& arg1, float arg2) {
|
||||
return i.method2(arg1, arg2);
|
||||
}
|
||||
|
||||
void emval_test_call_method3(Interface& i) {
|
||||
i.method3();
|
||||
}
|
||||
|
||||
void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
|
||||
v(i, f, tv, sv);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(([]() {
|
||||
function("mallinfo", &emval_test_mallinfo);
|
||||
|
||||
function("emval_test_new_integer", &emval_test_new_integer);
|
||||
function("emval_test_new_string", &emval_test_new_string);
|
||||
function("emval_test_new_object", &emval_test_new_object);
|
||||
function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned);
|
||||
function("emval_test_passthrough", &emval_test_passthrough);
|
||||
function("emval_test_return_void", &emval_test_return_void);
|
||||
function("emval_test_not", &emval_test_not);
|
||||
|
||||
function("emval_test_as_unsigned", &emval_test_as_unsigned);
|
||||
function("emval_test_get_length", &emval_test_get_length);
|
||||
function("emval_test_add", &emval_test_add);
|
||||
function("emval_test_sum", &emval_test_sum);
|
||||
|
||||
//function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
|
||||
function("emval_test_take_and_return_std_string", &emval_test_take_and_return_std_string);
|
||||
function("emval_test_take_and_return_std_string_const_ref", &emval_test_take_and_return_std_string_const_ref);
|
||||
|
||||
//function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct);
|
||||
|
||||
value_tuple<TupleVector>("TupleVector")
|
||||
.element(&TupleVector::x)
|
||||
.element(&TupleVector::y)
|
||||
//.element(&TupleVector::z)
|
||||
.element(&readTupleVectorZ, &writeTupleVectorZ)
|
||||
;
|
||||
|
||||
function("emval_test_return_TupleVector", &emval_test_return_TupleVector);
|
||||
function("emval_test_take_and_return_TupleVector", &emval_test_take_and_return_TupleVector);
|
||||
|
||||
value_tuple<TupleVectorTuple>("TupleVectorTuple")
|
||||
.element(&TupleVectorTuple::v)
|
||||
;
|
||||
|
||||
function("emval_test_return_TupleVectorTuple", &emval_test_return_TupleVectorTuple);
|
||||
|
||||
value_struct<StructVector>("StructVector")
|
||||
.field("x", &StructVector::x)
|
||||
.field("y", &StructVector::y)
|
||||
.field("z", &StructVector::z)
|
||||
;
|
||||
|
||||
function("emval_test_return_StructVector", &emval_test_return_StructVector);
|
||||
function("emval_test_take_and_return_StructVector", &emval_test_take_and_return_StructVector);
|
||||
|
||||
value_struct<TupleInStruct>("TupleInStruct")
|
||||
.field("field", &TupleInStruct::field)
|
||||
;
|
||||
|
||||
function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct);
|
||||
|
||||
class_<ValHolder>("ValHolder")
|
||||
.constructor<val>()
|
||||
.method("getVal", &ValHolder::getVal)
|
||||
.method("setVal", &ValHolder::setVal)
|
||||
.classmethod("some_class_method", &ValHolder::some_class_method)
|
||||
;
|
||||
function("emval_test_return_ValHolder", &emval_test_return_ValHolder);
|
||||
function("emval_test_set_ValHolder_to_empty_object", &emval_test_set_ValHolder_to_empty_object);
|
||||
|
||||
class_<StringHolder>("StringHolder")
|
||||
.constructor<std::string>()
|
||||
.method("set", &StringHolder::set)
|
||||
.method("get", &StringHolder::get)
|
||||
;
|
||||
|
||||
class_<CustomStruct>("CustomStruct")
|
||||
.constructor<>()
|
||||
.field("field", &CustomStruct::field)
|
||||
;
|
||||
|
||||
enum_<Enum>("Enum")
|
||||
.value("ONE", ONE)
|
||||
.value("TWO", TWO)
|
||||
;
|
||||
function("emval_test_take_and_return_Enum", &emval_test_take_and_return_Enum);
|
||||
|
||||
enum_<EnumClass>("EnumClass")
|
||||
.value("ONE", EnumClass::ONE)
|
||||
.value("TWO", EnumClass::TWO)
|
||||
;
|
||||
function("emval_test_take_and_return_EnumClass", &emval_test_take_and_return_EnumClass);
|
||||
|
||||
class InterfaceWrapper : public wrapper<Interface> {
|
||||
int method() {
|
||||
return call<int>("method");
|
||||
}
|
||||
TupleInStruct method2(const TupleInStruct& arg1, float arg2) {
|
||||
return call<TupleInStruct>("method2", arg1, arg2);
|
||||
}
|
||||
void method3() {
|
||||
return call<void>("method3");
|
||||
}
|
||||
};
|
||||
interface<InterfaceWrapper>("Interface")
|
||||
;
|
||||
function("emval_test_call_method", &emval_test_call_method);
|
||||
function("emval_test_call_method2", &emval_test_call_method2);
|
||||
function("emval_test_call_method3", &emval_test_call_method3);
|
||||
|
||||
function("emval_test_call_function", &emval_test_call_function);
|
||||
}));
|
|
@ -0,0 +1,341 @@
|
|||
module({
|
||||
Emscripten: '../build/Emscripten.js'
|
||||
}, function(imports) {
|
||||
var cm = imports.Emscripten;
|
||||
|
||||
var checkForLeaks = {
|
||||
setUp: function() {
|
||||
this.originalBlockCount = cm.mallinfo().uordblks;
|
||||
},
|
||||
tearDown: function() {
|
||||
assert.equal(this.originalBlockCount, cm.mallinfo().uordblks);
|
||||
},
|
||||
};
|
||||
|
||||
fixture("embind", {
|
||||
baseFixture: checkForLeaks,
|
||||
|
||||
"test value creation": function() {
|
||||
assert.equal(15, cm.emval_test_new_integer());
|
||||
assert.equal("Hello everyone", cm.emval_test_new_string());
|
||||
|
||||
var object = cm.emval_test_new_object();
|
||||
assert.equal('bar', object.foo);
|
||||
assert.equal(1, object.baz);
|
||||
},
|
||||
|
||||
"test passthrough": function() {
|
||||
var a = {foo: 'bar'};
|
||||
var b = cm.emval_test_passthrough(a);
|
||||
a.bar = 'baz';
|
||||
assert.equal('baz', b.bar);
|
||||
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test void return converts to undefined": function() {
|
||||
assert.equal(undefined, cm.emval_test_return_void());
|
||||
},
|
||||
|
||||
"test booleans can be marshalled": function() {
|
||||
assert.equal(false, cm.emval_test_not(true));
|
||||
assert.equal(true, cm.emval_test_not(false));
|
||||
},
|
||||
|
||||
"test convert double to unsigned": function() {
|
||||
var rv = cm.emval_test_as_unsigned(1.5);
|
||||
assert.equal('number', typeof rv);
|
||||
assert.equal(1, rv);
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test get length of array": function() {
|
||||
assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd']));
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test add a bunch of things": function() {
|
||||
assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test sum array": function() {
|
||||
assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test strings": function() {
|
||||
assert.equal("foobar", "foo" + "bar");
|
||||
assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar"));
|
||||
|
||||
assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar"));
|
||||
},
|
||||
|
||||
"test no memory leak when passing strings in by const reference": function() {
|
||||
var original = cm.mallinfo().uordblks;
|
||||
cm.emval_test_take_and_return_std_string_const_ref("foobar");
|
||||
assert.equal(original, cm.mallinfo().uordblks);
|
||||
},
|
||||
});
|
||||
|
||||
fixture("classes", {
|
||||
baseFixture: checkForLeaks,
|
||||
|
||||
"test class instance": function() {
|
||||
var a = {foo: 'bar'};
|
||||
var c = new cm.ValHolder(a);
|
||||
assert.equal('bar', c.getVal().foo);
|
||||
|
||||
c.setVal('1234');
|
||||
assert.equal('1234', c.getVal());
|
||||
|
||||
c.delete();
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test class methods": function() {
|
||||
assert.equal(10, cm.ValHolder.some_class_method(10));
|
||||
},
|
||||
|
||||
"test can't call methods on deleted class instances": function() {
|
||||
var c = new cm.ValHolder(undefined);
|
||||
c.delete();
|
||||
assert.throws(cm.BindingError, function() {
|
||||
c.getVal();
|
||||
});
|
||||
assert.throws(cm.BindingError, function() {
|
||||
c.delete();
|
||||
});
|
||||
},
|
||||
|
||||
"test isinstance": function() {
|
||||
var c = new cm.ValHolder(undefined);
|
||||
assert.instanceof(c, cm.ValHolder);
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test can return class instances by value": function() {
|
||||
var c = cm.emval_test_return_ValHolder();
|
||||
assert.deepEqual({}, c.getVal());
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test can pass class instances to functions by reference": function() {
|
||||
var a = {a:1};
|
||||
var c = new cm.ValHolder(a);
|
||||
cm.emval_test_set_ValHolder_to_empty_object(c);
|
||||
assert.deepEqual({}, c.getVal());
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test can access struct fields": function() {
|
||||
var c = new cm.CustomStruct();
|
||||
assert.equal(10, c.field);
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test can set struct fields": function() {
|
||||
var c = new cm.CustomStruct();
|
||||
c.field = 15;
|
||||
assert.equal(15, c.field);
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test assignment returns value": function() {
|
||||
var c = new cm.CustomStruct();
|
||||
assert.equal(15, c.field = 15);
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test assigning string to integer raises TypeError": function() {
|
||||
var c = new cm.CustomStruct();
|
||||
|
||||
var e = assert.throws(TypeError, function() {
|
||||
c.field = "hi";
|
||||
});
|
||||
assert.equal('Cannot convert "hi" to int', e.message);
|
||||
|
||||
var e = assert.throws(TypeError, function() {
|
||||
c.field = {foo:'bar'};
|
||||
});
|
||||
assert.equal('Cannot convert "[object Object]" to int', e.message);
|
||||
|
||||
c.delete();
|
||||
},
|
||||
|
||||
"test can return tuples by value": function() {
|
||||
var c = cm.emval_test_return_TupleVector();
|
||||
assert.deepEqual([1, 2, 3], c);
|
||||
},
|
||||
|
||||
"test tuples can contain tuples": function() {
|
||||
var c = cm.emval_test_return_TupleVectorTuple();
|
||||
assert.deepEqual([[1, 2, 3]], c);
|
||||
},
|
||||
|
||||
"test can pass tuples by value": function() {
|
||||
var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]);
|
||||
assert.deepEqual([4, 5, 6], c);
|
||||
},
|
||||
|
||||
"test can return structs by value": function() {
|
||||
var c = cm.emval_test_return_StructVector();
|
||||
assert.deepEqual({x: 1, y: 2, z: 3}, c);
|
||||
},
|
||||
|
||||
"test can pass structs by value": function() {
|
||||
var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6});
|
||||
assert.deepEqual({x: 4, y: 5, z: 6}, c);
|
||||
},
|
||||
|
||||
"test can pass and return tuples in structs": function() {
|
||||
var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]});
|
||||
assert.deepEqual({field: [1, 2, 3]}, d);
|
||||
},
|
||||
|
||||
"test can clone handles": function() {
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
|
||||
var a = new cm.ValHolder({});
|
||||
var b = a.clone();
|
||||
a.delete();
|
||||
|
||||
assert.equal(1, cm.count_emval_handles());
|
||||
|
||||
assert.throws(cm.BindingError, function() {
|
||||
a.delete();
|
||||
});
|
||||
b.delete();
|
||||
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test can't clone if already deleted": function() {
|
||||
var a = new cm.ValHolder({});
|
||||
a.delete();
|
||||
assert.throws(cm.BindingError, function() {
|
||||
a.clone();
|
||||
});
|
||||
},
|
||||
|
||||
"test moving handles is a clone+delete": function() {
|
||||
var a = new cm.ValHolder({});
|
||||
var b = a.move();
|
||||
assert.throws(cm.BindingError, function() {
|
||||
a.delete();
|
||||
});
|
||||
assert.equal(1, cm.count_emval_handles());
|
||||
b.delete();
|
||||
assert.equal(0, cm.count_emval_handles());
|
||||
},
|
||||
|
||||
"test StringHolder": function() {
|
||||
var a = new cm.StringHolder("foobar");
|
||||
assert.equal("foobar", a.get());
|
||||
|
||||
a.set("barfoo");
|
||||
assert.equal("barfoo", a.get());
|
||||
a.delete();
|
||||
},
|
||||
});
|
||||
|
||||
fixture("embind enumerations", {
|
||||
baseFixture: checkForLeaks,
|
||||
|
||||
"test can compare enumeration values": function() {
|
||||
assert.equal(cm.Enum.ONE, cm.Enum.ONE);
|
||||
assert.notEqual(cm.Enum.ONE, cm.Enum.TWO);
|
||||
},
|
||||
|
||||
"test repr includes enum value": function() {
|
||||
assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE));
|
||||
assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO));
|
||||
},
|
||||
|
||||
"test instanceof": function() {
|
||||
assert.instanceof(cm.Enum.ONE, cm.Enum);
|
||||
},
|
||||
|
||||
"test can pass and return enumeration values to functions": function() {
|
||||
assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO));
|
||||
},
|
||||
});
|
||||
|
||||
fixture("C++11 enum class", {
|
||||
baseFixture: checkForLeaks,
|
||||
|
||||
"test can compare enumeration values": function() {
|
||||
assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE);
|
||||
assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO);
|
||||
},
|
||||
|
||||
"test repr includes enum value": function() {
|
||||
assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE));
|
||||
assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO));
|
||||
},
|
||||
|
||||
"test instanceof": function() {
|
||||
assert.instanceof(cm.EnumClass.ONE, cm.EnumClass);
|
||||
},
|
||||
|
||||
"test can pass and return enumeration values to functions": function() {
|
||||
assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO));
|
||||
},
|
||||
});
|
||||
|
||||
fixture("emval call tests", {
|
||||
"test can call functions from C++": function() {
|
||||
var called = false;
|
||||
cm.emval_test_call_function(function(i, f, tv, sv) {
|
||||
called = true;
|
||||
assert.equal(10, i);
|
||||
assert.equal(1.5, f);
|
||||
assert.deepEqual([1.25, 2.5, 3.75], tv);
|
||||
assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv);
|
||||
}, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75});
|
||||
assert.true(called);
|
||||
},
|
||||
});
|
||||
|
||||
fixture("interfaces", {
|
||||
baseFixture: checkForLeaks,
|
||||
|
||||
"test can wrap JS object in native interface": function() {
|
||||
var foo = {
|
||||
calls: [],
|
||||
method: function() {
|
||||
this.calls.push('called');
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
|
||||
assert.equal(10, cm.emval_test_call_method(foo));
|
||||
assert.deepEqual(['called'], foo.calls);
|
||||
},
|
||||
|
||||
"test can pass arguments and return complicated values": function() {
|
||||
var calls = [];
|
||||
var foo = {
|
||||
method2: function(arg1, arg2) {
|
||||
calls.push([arg1, arg2]);
|
||||
return arg1;
|
||||
}
|
||||
};
|
||||
|
||||
var result = cm.emval_test_call_method2(foo, {field: [1, 2, 3]}, 7);
|
||||
assert.deepEqual({field: [1, 2, 3]}, result);
|
||||
assert.deepEqual([[{field: [1, 2, 3]}, 7]], calls);
|
||||
},
|
||||
|
||||
"test can call interface methods that return nothing": function() {
|
||||
var calls = [];
|
||||
var foo = {
|
||||
method3: function() {
|
||||
calls.push('called');
|
||||
}
|
||||
};
|
||||
cm.emval_test_call_method3(foo);
|
||||
assert.deepEqual(['called'], calls);
|
||||
},
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче