Skip to content

Commit

Permalink
WebIDL Binder: Add 'BindTo' extended attribute to pick the called fun…
Browse files Browse the repository at this point in the history
…ction (emscripten-core#22779)

This attribute can be used to change the C++ function that will be
called. It can be used expose functions that have multiple overloads
with the same number of parameters.

Example:

// C++
class BindToTest {
public:
  int test(const char*) { return 1; }
  int test(int) { return 2; }
};

// WebIDL
interface BindToTest {
  void BindToTest();
  [BindTo="test"] long testString([Const] DOMString arg);
  [BindTo="test"] long testInt(long arg);
};
  • Loading branch information
jrouwe authored Oct 25, 2024
1 parent 12cedf4 commit aa3a8a4
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,55 @@ When C++ code has a pointer to a ``Base`` instance and calls ``virtualFunc()``,
- You *must* implement all the methods you mentioned in the IDL of the ``JSImplementation`` class (``ImplJS``) or compilation will fail with an error.
- You will also need to provide an interface definition for the ``Base`` class in the IDL file.

Function overloads
==================

C++ allows function overloads, where multiple member functions have the same name but different arguments. By default, the *WebIDL Binder* allows you to bind overloaded functions if they differ only in the number of arguments:

.. code-block:: cpp
// C++
class OverloadTest {
public:
void test(int arg1, int arg2) { ... }
void test(int arg) { ... }
};
.. code-block:: idl
// WebIDL
interface OverloadTest {
void OverloadTest();
void test(long arg1, long arg2);
void test(long arg);
};
If your overloaded functions differ in some other way (say, in the types) then you can use the ``[BindTo]`` attribute to tell the tool what function name to bind to (that is, to call):

.. code-block:: cpp
// C++
class BindToTest {
public:
void test(const char* arg) { ... }
void test(int arg) { ... }
};
.. code-block:: idl
// WebIDL
interface BindToTest {
void BindToTest();
[BindTo="test"] void testString([Const] DOMString arg);
[BindTo="test"] void testInt(long arg);
};
In this case the C++ function ``test(const char*)`` will be named ``testString`` in JavaScript and ``test(int)`` will be named ``testInt``.

.. note::

You can also use ``[BindTo]`` to just rename a function, e.g. if you want to rename ``MyFunctionName`` to ``myFunctionName``.

Pointers and comparisons
=========================

Expand Down
4 changes: 4 additions & 0 deletions test/webidl/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ new TheModule.Inner().incInPlace(new TheModule.Inner());
console.log('add: ' + new TheModule.Inner(1).add(new TheModule.Inner(2)).get_value());
console.log('mul2: ' + new TheModule.Inner(10).mul2(5));

let bindTo = new TheModule.BindToTest();
console.log('testString: ' + bindTo.testString('hello'));
console.log('testInt: ' + bindTo.testInt(10));

console.log(TheModule.enum_value1);
console.log(TheModule.enum_value2);

Expand Down
6 changes: 6 additions & 0 deletions test/webidl/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,9 @@ class ArrayArgumentTest {
private:
const char* m_array;
};

class BindToTest {
public:
int test(const char*) { return 1; }
int test(int) { return 2; }
};
6 changes: 6 additions & 0 deletions test/webidl/test.idl
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,9 @@ interface JSArrayArgumentTest {
boolean byteArrayTest([Const] byte[] arg);
boolean domStringTest([Const] DOMString arg);
};

interface BindToTest {
void BindToTest();
[BindTo="test"] long testString([Const] DOMString arg);
[BindTo="test"] long testInt(long arg);
};
2 changes: 2 additions & 0 deletions test/webidl/test_ALL.out
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ getAsArray: 24
Inner::+= => 2
add: 3
mul2: 50
testString: 1
testInt: 2
0
1
34,34
Expand Down
2 changes: 2 additions & 0 deletions test/webidl/test_DEFAULT.out
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ getAsArray: 24
Inner::+= => 2
add: 3
mul2: 50
testString: 1
testInt: 2
0
1
34,34
Expand Down
2 changes: 2 additions & 0 deletions test/webidl/test_FAST.out
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ getAsArray: 24
Inner::+= => 2
add: 3
mul2: 50
testString: 1
testInt: 2
0
1
34,34
Expand Down
3 changes: 2 additions & 1 deletion third_party/WebIDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -3443,7 +3443,8 @@ def handleExtendedAttribute(self, attr):
identifier == "Value" or
identifier == "Operator" or
identifier == "Const" or
identifier == "WebGLHandlesContextLoss"):
identifier == "WebGLHandlesContextLoss" or
identifier == "BindTo"):
# Known attributes that we don't need to do anything with here
pass
else:
Expand Down
11 changes: 7 additions & 4 deletions tools/webidl_binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ def type_to_cdec(raw):

def render_function(class_name, func_name, sigs, return_type, non_pointer,
copy, operator, constructor, is_static, func_scope,
call_content=None, const=False, array_attribute=False):
call_content=None, const=False, array_attribute=False,
bind_to=None):
legacy_mode = CHECKS not in ['ALL', 'FAST']
all_checks = CHECKS == 'ALL'

Expand Down Expand Up @@ -601,9 +602,10 @@ def make_call_args(i):
elif call_content is not None:
call = call_content
else:
call = func_name + '(' + call_args + ')'
if not bind_to:
bind_to = func_name
call = bind_to + '(' + call_args + ')'
if is_static:

call = c_class_name + '::' + call
else:
call = 'self->' + call
Expand Down Expand Up @@ -771,7 +773,8 @@ def add_bounds_check_impl():
constructor,
is_static=m.isStatic(),
func_scope=m.parentScope.identifier.name,
const=m.getExtendedAttribute('Const'))
const=m.getExtendedAttribute('Const'),
bind_to=(m.getExtendedAttribute('BindTo') or [None])[0])
mid_js += ['\n']
if constructor:
mid_js += build_constructor(name)
Expand Down

0 comments on commit aa3a8a4

Please sign in to comment.