Merge pull request #592 from imvu/embind-pull-request

Embind pull request
This commit is contained in:
Alon Zakai 2012-10-01 10:48:28 -07:00
Родитель 7eaa78060c 58056b5383
Коммит 375982d9c1
8 изменённых файлов: 2485 добавлений и 0 удалений

619
src/embind/embind.js Normal file
Просмотреть файл

@ -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;
},
};
}

111
src/embind/emval.js Normal file
Просмотреть файл

@ -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);
}
}
}

34
system/lib/embind/bind.cpp Executable file
Просмотреть файл

@ -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);
}));

341
tests/embind/embind_test.js Normal file
Просмотреть файл

@ -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);
},
});
});