diff options
-rwxr-xr-x | src/embind/embind.js | 44 | ||||
-rwxr-xr-x | system/include/emscripten/bind.h | 34 | ||||
-rwxr-xr-x | tests/embind/embind.test.js | 21 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 18 |
4 files changed, 99 insertions, 18 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js index c26a463e..1b8b2456 100755 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -1417,40 +1417,54 @@ function __embind_register_class_property( ) { fieldName = readLatin1String(fieldName); getter = FUNCTION_TABLE[getter]; - setter = FUNCTION_TABLE[setter]; whenDependentTypesAreResolved([], [classType], function(classType) { classType = classType[0]; var humanName = classType.name + '.' + fieldName; - - Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { + var desc = { get: function() { throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); }, - set: function() { - throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); - }, enumerable: true, configurable: true - }); + }; + if (setter) { + desc.set = function() { + throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); + }; + } else { + desc.set = function(v) { + throwBindingError(humanName + ' is a read-only property'); + }; + } - whenDependentTypesAreResolved([], [getterReturnType, setterArgumentType], function(types) { - var getterReturnType = types[0]; - var setterArgumentType = types[1]; + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); - Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, { + whenDependentTypesAreResolved( + [], + (setter ? [getterReturnType, setterArgumentType] : [getterReturnType]), + function(types) { + var getterReturnType = types[0]; + var desc = { get: function() { var ptr = validateThis(this, classType, humanName + ' getter'); return getterReturnType.fromWireType(getter(getterContext, ptr)); }, - set: function(v) { + enumerable: true + }; + + if (setter) { + setter = FUNCTION_TABLE[setter]; + var setterArgumentType = types[1]; + desc.set = function(v) { var ptr = validateThis(this, classType, humanName + ' setter'); var destructors = []; setter(setterContext, ptr, setterArgumentType.toWireType(destructors, v)); runDestructors(destructors); - }, - enumerable: true - }); + }; + } + + Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc); return []; }); diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index a3886031..0bb21290 100755 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -991,7 +991,23 @@ namespace emscripten { return *this; } - template<typename FieldType> + template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type> + class_& property(const char* fieldName, const FieldType ClassType::*field) { + using namespace internal; + + _embind_register_class_property( + TypeID<ClassType>::get(), + fieldName, + TypeID<FieldType>::get(), + reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>), + getContext(field), + 0, + 0, + 0); + return *this; + } + + template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type> class_& property(const char* fieldName, FieldType ClassType::*field) { using namespace internal; @@ -1007,6 +1023,22 @@ namespace emscripten { return *this; } + template<typename Getter> + class_& property(const char* fieldName, Getter getter) { + using namespace internal; + typedef GetterPolicy<Getter> GP; + _embind_register_class_property( + TypeID<ClassType>::get(), + fieldName, + TypeID<typename GP::ReturnType>::get(), + reinterpret_cast<GenericFunction>(&GP::template get<ClassType>), + GP::getContext(getter), + 0, + 0, + 0); + return *this; + } + template<typename Getter, typename Setter> class_& property(const char* fieldName, Getter getter, Setter setter) { using namespace internal; diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js index 4ad854f4..8ef46ad8 100755 --- a/tests/embind/embind.test.js +++ b/tests/embind/embind.test.js @@ -916,6 +916,27 @@ module({ c.delete(); }); + test("class properties can be read-only", function() { + var a = {}; + var h = new cm.ValHolder(a); + assert.equal(a, h.val_readonly); + var e = assert.throws(cm.BindingError, function() { + h.val_readonly = 10; + }); + assert.equal('ValHolder.val_readonly is a read-only property', e.message); + h.delete(); + }); + + test("read-only member field", function() { + var a = new cm.HasReadOnlyProperty(10); + assert.equal(10, a.i); + var e = assert.throws(cm.BindingError, function() { + a.i = 20; + }); + assert.equal('HasReadOnlyProperty.i is a read-only property', e.message); + a.delete(); + }); + test("class instance $$ property is non-enumerable", function() { var c = new cm.ValHolder(undefined); assert.deepEqual([], Object.keys(c)); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 0439c832..7cde8e7f 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -673,8 +673,6 @@ private: std::string name_;
};
-// todo: does it need to be polymorphic?
-// todo: virtual diamond pattern
class PolyDiamondBase {
public:
PolyDiamondBase():
@@ -1551,6 +1549,7 @@ EMSCRIPTEN_BINDINGS(tests) { .function("getValConstRef", &ValHolder::getValConstRef)
.function("setVal", &ValHolder::setVal)
.property("val", &ValHolder::getVal, &ValHolder::setVal)
+ .property("val_readonly", &ValHolder::getVal)
.class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer<ret_val>())
.class_function("makeValHolder", &ValHolder::makeValHolder)
.class_function("some_class_method", &ValHolder::some_class_method)
@@ -2096,3 +2095,18 @@ EMSCRIPTEN_BINDINGS(noncopyable) { function("getNoncopyable", &getNoncopyable);
}
+
+struct HasReadOnlyProperty {
+ HasReadOnlyProperty(int i)
+ : i(i)
+ {}
+
+ const int i;
+};
+
+EMSCRIPTEN_BINDINGS(read_only_properties) {
+ class_<HasReadOnlyProperty>("HasReadOnlyProperty")
+ .constructor<int>()
+ .property("i", &HasReadOnlyProperty::i)
+ ;
+}
|