webidl binder: define properties with JS accessors (#7298)
Currently C++ class and struct properties are defined in JavaScript bindings with get_foo and set_foo accessor methods. This PR adds support for directly accessing the properties using native JS accessors. For example:
// Current way
myObject.set_foo(1);
console.log(myObject.get_foo());
// After this PR:
myObject.foo = 1;
console.log(myObject.foo);
This is more idiomatic JavaScript, and means that the bindings match the IDL correctly. I have left the existing getters and setters in place, so this is be backward-compatible.
diff --git a/AUTHORS b/AUTHORS
index 67869e8..db8d710 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -364,3 +364,4 @@
* Zoltán Žarkov <zeko@freecivweb.org>
* Roman Yurchak <rth.yurchak@pm.me>
* Hampton Maxwell <me@hamptonmaxwell.com>
+* Matt Kane <m@mk.gg>
diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst b/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst
index 41621f5..015d10f 100644
--- a/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst
+++ b/site/source/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.rst
@@ -171,6 +171,33 @@
You will usually need to destroy the objects which you create, but this depends on the library being ported.
+Attributes
+==========
+
+Object attributes are defined in IDL using the ``attribute`` keyword. These can then be accessed in JavaScript using either ``get_foo()``/``set_foo()`` accessor methods, or directly as a property of the object.
+
+.. code-block:: cpp
+
+ // C++
+ int attr;
+
+.. code-block:: idl
+
+ // WebIDL
+ attribute long attr;
+
+.. code-block:: javascript
+
+ // JavaScript
+ var f = new Module.Foo();
+ f.attr = 7;
+ // Equivalent to:
+ f.set_attr(7);
+
+ console.log(f.attr);
+ console.log(f.get_attr());
+
+For read-only attributes, see :ref:`webidl-binder-const`.
Pointers, References, Value types (Ref and Value)
====================================================
@@ -215,7 +242,7 @@
// WebIDL
[Value] MyClass process([Ref] MyClass input);
-
+.. _webidl-binder-const:
Const
=====
@@ -246,7 +273,7 @@
// WebIDL
readonly attribute long numericalConstant;
-This will generate a ``get_numericalConstant()`` method in the bindings, but not a corresponding setter.
+This will generate a ``get_numericalConstant()`` method in the bindings, but not a corresponding setter. The attribute will also be defined as read-only in JavaScript, meaning that trying to set it will have no effect on the value, and will throw an error in strict mode.
.. tip:: It is possible for a return type to have multiple specifiers. For example, an method that returns a contant reference would be marked up in the IDL using ``[Ref, Const]``.
diff --git a/tests/webidl/output_ALL.txt b/tests/webidl/output_ALL.txt
index c50dd16..8be8599 100644
--- a/tests/webidl/output_ALL.txt
+++ b/tests/webidl/output_ALL.txt
@@ -4,6 +4,13 @@
object
object
8
+8
+8
+6
+6
+9
+10
+10
boolean
true
c1
diff --git a/tests/webidl/output_DEFAULT.txt b/tests/webidl/output_DEFAULT.txt
index d0d3faf..04335b4 100644
--- a/tests/webidl/output_DEFAULT.txt
+++ b/tests/webidl/output_DEFAULT.txt
@@ -4,6 +4,13 @@
object
object
8
+8
+8
+6
+6
+9
+10
+10
boolean
true
c1
diff --git a/tests/webidl/output_FAST.txt b/tests/webidl/output_FAST.txt
index d0d3faf..04335b4 100644
--- a/tests/webidl/output_FAST.txt
+++ b/tests/webidl/output_FAST.txt
@@ -4,6 +4,13 @@
object
object
8
+8
+8
+6
+6
+9
+10
+10
boolean
true
c1
diff --git a/tests/webidl/post.js b/tests/webidl/post.js
index 445a145..a1872c8 100644
--- a/tests/webidl/post.js
+++ b/tests/webidl/post.js
@@ -9,6 +9,20 @@
console.log(typeof sme.getAsConst());
console.log(typeof sme.voidStar(sme));
console.log(sme.get_immutableAttr());
+console.log(sme.immutableAttr);
+
+try {
+ sme.immutableAttr = 1;
+} catch(e) {}
+console.log(sme.immutableAttr); // Should be unchanged
+console.log(sme.attr);
+console.log(sme.get_attr());
+sme.attr = 9;
+console.log(sme.attr);
+sme.set_attr(10);
+console.log(sme.attr);
+console.log(sme.get_attr());
+
console.log(typeof sme.getBoolean());
console.log(sme.getBoolean());
diff --git a/tests/webidl/test.cpp b/tests/webidl/test.cpp
index 8a3fe70..8d4b97e 100644
--- a/tests/webidl/test.cpp
+++ b/tests/webidl/test.cpp
@@ -5,8 +5,8 @@
#include "test.h"
-Parent::Parent(int val) : value(val), immutableAttr(8) { printf("Parent:%d\n", val); }
-Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value), immutableAttr(8) { printf("Parent:%d\n", value); }
+Parent::Parent(int val) : value(val), immutableAttr(8), attr(6) { printf("Parent:%d\n", val); }
+Parent::Parent(Parent *p, Parent *q) : value(p->value + q->value), immutableAttr(8), attr(6) { printf("Parent:%d\n", value); }
void Parent::mulVal(int mul) { value *= mul; }
typedef EnumClass::EnumWithinClass EnumClass_EnumWithinClass;
diff --git a/tests/webidl/test.h b/tests/webidl/test.h
index 8bfa398..20c770a 100644
--- a/tests/webidl/test.h
+++ b/tests/webidl/test.h
@@ -15,7 +15,7 @@
const Parent *getAsConst() { return NULL; }
void *voidStar(void *something) { return something; }
bool getBoolean() { return true; }
-
+ int attr;
const int immutableAttr;
};
diff --git a/tests/webidl/test.idl b/tests/webidl/test.idl
index 5ceda1d..b37a511 100644
--- a/tests/webidl/test.idl
+++ b/tests/webidl/test.idl
@@ -9,7 +9,7 @@
[Const] Parent getAsConst();
VoidPtr voidStar(VoidPtr something);
boolean getBoolean();
-
+ attribute long attr;
readonly attribute long immutableAttr;
};
diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py
index 54b788b..cde81fe 100644
--- a/tools/webidl_binder.py
+++ b/tools/webidl_binder.py
@@ -683,7 +683,10 @@
const=m.getExtendedAttribute('Const'),
array_attribute=m.type.isArray())
- if not m.readonly:
+ if m.readonly:
+ mid_js += [r'''
+ Object.defineProperty(%s.prototype, '%s', { get: %s.prototype.%s }) ''' % (name, attr, name, get_name)]
+ else:
set_name = 'set_' + attr
mid_js += [r'''
%s.prototype['%s'] = %s.prototype.%s = ''' % (name, set_name, name, set_name)]
@@ -697,6 +700,9 @@
call_content=set_call_content,
const=m.getExtendedAttribute('Const'),
array_attribute=m.type.isArray())
+ mid_js += [r'''
+ Object.defineProperty(%s.prototype, '%s', { get: %s.prototype.%s, set: %s.prototype.%s }) ''' % (name, attr, name, get_name, name, set_name)]
+
if not interface.getExtendedAttribute('NoDelete'):
mid_js += [r'''