aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/embind/embind.js44
-rwxr-xr-xsystem/include/emscripten/bind.h34
-rwxr-xr-xtests/embind/embind.test.js21
-rw-r--r--tests/embind/embind_test.cpp18
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)
+ ;
+}