#include <stdio.h>
#include <functional>
#include <emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
// Counters to track Value structs lifetimes.
int default_constructed = 0, copy_constructed = 0,
move_constructed = 0, destructed = 0;
struct Value {
std::string name;
Value() : name("initial") {
printf("create\n");
default_constructed++;
}
Value(const Value &v) {
printf("copy\n");
name = v.name;
copy_constructed++;
}
Value(Value&& v) {
printf("move\n");
name = std::move(v.name);
move_constructed++;
}
Value& operator=(Value other) {
printf("copy assign\n");
return *this;
}
std::string getName() {
return name;
}
void setName(std::string _name) {
name = _name;
}
~Value() {
printf("destroy\n");
destructed++;
}
};
void resetCounters() {
default_constructed = 0;
copy_constructed = 0,
move_constructed = 0;
destructed = 0;
}
void assertDefault(int count) {
assert(count == default_constructed);
default_constructed = 0;
}
void assertCopy(int count) {
assert(count == copy_constructed);
copy_constructed = 0;
}
void assertMove(int count) {
assert(count == move_constructed);
move_constructed = 0;
}
void assertDestructed(int count) {
assert(count == destructed);
destructed = 0;
}
void assertAllCountsZero() {
assert(default_constructed == 0);
assert(copy_constructed == 0);
assert(move_constructed == 0);
assert(destructed == 0);
}
struct ValueHolder {
ValueHolder() {};
Value value;
Value* valuePtr;
Value* getPtr() {
return &value;
}
Value getValue() {
return value;
}
Value& getRef() {
return value;
}
void set(Value v) {
value = v;
}
Value get() const {
return value;
}
void initializePtr() {
valuePtr = new Value();
}
Value* getNewPtr() const {
return new Value();
}
const Value* getNewPtrConst() const {
return new Value();
}
void setPtr(Value* v) {
valuePtr = v;
}
Value* getPtrConst() const {
return valuePtr;
}
static Value* staticGetPtr() {
static Value v;
return &v;
}
};
const Value* value_holder(const ValueHolder& target) {
return target.getNewPtrConst();
}
Value* _valuePtr;
Value _value;
void initializeValuePtr() {
_valuePtr = new Value();
}
void resetValue() {
_value.name = "initial";
}
void deleteValue() {
delete _valuePtr;
}
Value* getPtr() {
return _valuePtr;
};
Value* getNewPtr() {
return new Value();
};
Value getValue() {
return Value();
};
Value& getRef() {
return _value;
};
int main() {
EM_ASM(
/////////////////////////////////
// Test RVP with plain functions.
/////////////////////////////////
Module.resetCounters();
console.log("getPtrTakeOwnership");
var value = Module.getPtrTakeOwnership();
value.delete();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("getPtrReference");
Module.initializeValuePtr();
value = Module.getPtrReference();
// Check that both references are updated.
value.setName("reference");
value = Module.getPtrReference();
assert(value.getName() === "reference");
// TODO: enforce that we can't delete it from JS.
Module.deleteValue();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("getValueDefault");
value = Module.getValueDefault();
value.delete();
Module.assertDefault(1);
Module.assertCopy(1);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("getValueTakeOwnership");
value = Module.getValueTakeOwnership();
value.delete();
Module.assertDefault(1);
Module.assertMove(1);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("getRefDefault");
value = Module.getRefDefault();
value.delete();
Module.assertCopy(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("getRefTakeOwnership");
value = Module.getRefTakeOwnership();
value.delete();
Module.assertMove(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("getRefReference");
value = Module.getRefReference();
Module.assertAllCountsZero();
///////////////////////////////
// Test RVP with class methods.
///////////////////////////////
console.log("Module.ValueHolder");
var valueHolder = new Module.ValueHolder();
Module.resetCounters();
console.log("valueHolder.getPtrTakeOwnership");
value = valueHolder.getPtrTakeOwnership();
value.delete();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("valueHolder.getPtrReference");
value = valueHolder.getPtrReference();
Module.assertAllCountsZero();
console.log("valueHolder.getPtrWithStdFunctionTakeOwnership");
value = valueHolder.getPtrWithStdFunctionTakeOwnership();
value.delete();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
valueHolder.delete();
////////////////////////////////////////
// Test RVP with static class functions.
////////////////////////////////////////
// These bindings use the same machinery as regular function bindings,
// so don't bother extensively testing them.
Module.resetCounters();
console.log("staticGetPtrReference");
value = Module.ValueHolder.staticGetPtrReference();
Module.assertDefault(1);
Module.assertAllCountsZero();
////////////////////////////////////////
// Test RVP with class properties.
////////////////////////////////////////
var valueHolder = new Module.ValueHolder();
Module.resetCounters();
console.log("valueHolder.valueDefault");
var originalValueProp = valueHolder.valueReference;
var valueProp = valueHolder.valueDefault;
valueProp.setName("copy");
assert(originalValueProp.getName() === "initial");
valueProp.delete();
Module.assertCopy(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("valueHolder.valueReference");
valueProp = valueHolder.valueReference;
assert(valueProp instanceof Module.Value);
// Check that both references are updated.
valueProp.setName("reference");
valueProp = valueHolder.valueReference;
assert(valueProp.getName() === "reference");
Module.assertAllCountsZero();
console.log("valueHolder.valueTakeOwnership");
var originalValueProp = valueHolder.valueReference;
valueProp = valueHolder.valueTakeOwnership;
valueProp.setName("move");
assert(originalValueProp.getName() === "");
valueProp.delete();
Module.assertMove(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("valueHolder.valuePtrReference");
valueHolder.initializeValuePtr();
valueProp = valueHolder.valuePtrReference;
Module.assertDefault(1);
Module.assertAllCountsZero();
console.log("valueHolder.valuePtrTakeOwnership");
valueHolder.initializeValuePtr();
valueProp = valueHolder.valuePtrTakeOwnership;
valueProp.delete();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
console.log("valueHolder.getterValueDefault");
valueProp = valueHolder.getterValueDefault;
assert(valueProp instanceof Module.Value);
valueProp.delete();
// TODO: Investigate if this can be made to only create one copy. The getter
// wrapper appears to create an extra copy.
Module.assertCopy(2);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("valueHolder.getterValueTakeOwnership");
valueProp = valueHolder.getterValueTakeOwnership;
assert(valueProp instanceof Module.Value);
valueProp.delete();
// TODO: Investigate if the copy can be removed.
Module.assertCopy(1);
Module.assertMove(1);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("valueHolder.getterValuePtrTakeOwnership");
valueProp = valueHolder.getterValuePtrTakeOwnership;
assert(valueProp instanceof Module.Value);
Module.assertAllCountsZero();
console.log("valueHolder.getterValuePtrReference");
valueProp = valueHolder.getterValuePtrReference;
assert(valueProp instanceof Module.Value);
Module.assertAllCountsZero();
console.log("valueHolder.getterSetterValueDefault");
valueProp = valueHolder.getterSetterValueDefault;
assert(valueProp instanceof Module.Value);
valueProp.delete();
// TODO: Investigate if this can be made to only create one copy. The getter
// wrapper appears to create an extra copy.
Module.assertCopy(2);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("valueHolder.getterSetterValueTakeOwnership");
valueProp = valueHolder.getterSetterValueTakeOwnership;
assert(valueProp instanceof Module.Value);
valueProp.delete();
// TODO: Investigate if the copy can be removed.
Module.assertCopy(1);
Module.assertMove(1);
Module.assertDestructed(2);
Module.assertAllCountsZero();
console.log("valueHolder.getterSetterPtrReference");
valueHolder.initializeValuePtr();
valueProp = valueHolder.getterSetterPtrReference;
assert(valueProp instanceof Module.Value);
Module.assertDefault(1);
Module.assertAllCountsZero();
console.log("valueHolder.getterSetterPtrTakeOwnership");
valueHolder.initializeValuePtr();
valueProp = valueHolder.getterSetterPtrTakeOwnership;
assert(valueProp instanceof Module.Value);
valueProp.delete();
Module.assertDefault(1);
Module.assertDestructed(1);
Module.assertAllCountsZero();
valueHolder.delete();
);
}
EMSCRIPTEN_BINDINGS(xxx) {
function("resetCounters", &resetCounters);
function("initializeValuePtr", &initializeValuePtr);
function("deleteValue", &deleteValue);
function("assertAllCountsZero", &assertAllCountsZero);
function("assertDefault", &assertDefault);
function("assertCopy", &assertCopy);
function("assertMove", &assertMove);
function("assertDestructed", &assertDestructed);
class_<Value>("Value")
.function("getName", &Value::getName)
.function("setName", &Value::setName);
// Skip the test for getPtr with a default return policy since raw pointers
// must explicitly choose a policy.
function("getPtrTakeOwnership", &getNewPtr, return_value_policy::take_ownership());
function("getPtrReference", &getPtr, return_value_policy::reference());
function("getValueDefault", &getValue);
function("getValueTakeOwnership", &getValue, return_value_policy::take_ownership());
// Skip the test for getValue with a reference return policy since it is not
// allowed.
function("getRefDefault", &getRef);
function("getRefTakeOwnership", &getRef, return_value_policy::take_ownership());
function("getRefReference", &getRef, return_value_policy::reference());
class_<ValueHolder>("ValueHolder")
.constructor<>()
.function("getPtrTakeOwnership", &ValueHolder::getNewPtr, return_value_policy::take_ownership())
.function("getPtrReference", &ValueHolder::getPtr, return_value_policy::reference())
.function("getPtrWithStdFunctionTakeOwnership", std::function<const Value*(const ValueHolder&)>(&value_holder), return_value_policy::take_ownership())
.class_function("staticGetPtrReference", &ValueHolder::staticGetPtr, return_value_policy::reference())
.function("initializeValuePtr", &ValueHolder::initializePtr)
// Test the code path for properties directly bound to a member.
.property("valueDefault", &ValueHolder::value)
.property("valueReference", &ValueHolder::value, return_value_policy::reference())
.property("valueTakeOwnership", &ValueHolder::value, return_value_policy::take_ownership())
// Test binding raw pointers with the various methods.
// Skip the test for default since that is forbidden.
.property("valuePtrReference", &ValueHolder::valuePtr, return_value_policy::reference())
.property("valuePtrTakeOwnership", &ValueHolder::valuePtr, return_value_policy::take_ownership())
// Getter method only with value.
.property("getterValueDefault", &ValueHolder::get)
// Skip the test for rvp::reference since that is forbidden.
.property("getterValueTakeOwnership", &ValueHolder::get, return_value_policy::take_ownership())
// Getter method only with pointer.
// Skip the test for default since that is not allowed.
.property("getterValuePtrReference", &ValueHolder::getPtrConst, return_value_policy::reference())
.property("getterValuePtrTakeOwnership", &ValueHolder::getPtrConst, return_value_policy::take_ownership())
// Getter and setter with value.
.property("getterSetterValueDefault", &ValueHolder::get, &ValueHolder::set)
// Skip the test for rvp::reference since that is forbidden.
.property("getterSetterValueTakeOwnership", &ValueHolder::get, &ValueHolder::set, return_value_policy::take_ownership())
// Getter and setter with pointer.
// Skip the test for default since that is not allowed.
.property("getterSetterPtrReference", &ValueHolder::getPtrConst, &ValueHolder::setPtr, return_value_policy::reference())
.property("getterSetterPtrTakeOwnership", &ValueHolder::getPtrConst, &ValueHolder::setPtr, return_value_policy::take_ownership())
// std::function getter.
.property("getterPtrWithStdFunctionReference", std::function<const Value*(const ValueHolder&)>(&value_holder), return_value_policy::reference())
;
}