Write documentation for EM_JS (#6506)
diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst
index 5337729..494701f 100644
--- a/site/source/docs/api_reference/emscripten.h.rst
+++ b/site/source/docs/api_reference/emscripten.h.rst
@@ -22,6 +22,91 @@
Defines
-------
+.. c:macro:: EM_JS(return_type, function_name, arguments, code)
+
+ Convenient syntax for JavaScript library functions.
+
+ This allows you to declare JavaScript in your C code as a function, which can
+ be called like a normal C function. For example, the following C program would
+ display two alerts if it was compiled with Emscripten and run in the browser: ::
+ EM_JS(void, two_alerts, (), {
+ alert('hai');
+ alert('bai');
+ });
+
+ int main() {
+ two_alerts();
+ return 0;
+ }
+
+ Arguments can be passed as normal C arguments, and have the same name in the
+ JavaScript code. These arguments can either be of type ``int32_t`` or
+ ``double``. ::
+ EM_JS(void, take_args, (int x, float y), {
+ console.log('I received: ' + [x, y]);
+ });
+
+ int main() {
+ take_args(100, 35.5);
+ return 0;
+ }
+
+ Null-terminated C strings can also be passed into ``EM_JS`` functions, but to
+ operate on them, they need to be copied out from the heap to convert to
+ high-level JavaScript strings. ::
+ EM_JS(void, say_hello, (const char* str), {
+ console.log('hello ' + UTF8ToString(str));
+ }
+
+ In the same manner, pointers to any type (including ``void *``) can be passed
+ inside ``EM_JS`` code, where they appear as integers like ``char *`` pointers
+ above did. Accessing the data can be managed by reading the heap directly. ::
+ EM_JS(void, read_data, (int* data), {
+ console.log('Data: ' + HEAP32[data>>2] + ', ' + HEAP32[(data+4)>>2]);
+ });
+
+ int main() {
+ int arr[2] = { 30, 45 };
+ read_data(arr);
+ return 0;
+ }
+
+ In addition, EM_JS functions can return a value back to C code. The output
+ value is passed back with a ``return`` statement: ::
+ EM_JS(int, add_forty_two, (int n), {
+ return n + 42;
+ });
+
+ EM_JS(int, get_total_memory, (), {
+ return TOTAL_MEMORY;
+ });
+
+ int main() {
+ int x = add_forty_two(100);
+ int y = get_total_memory();
+ // ...
+ }
+
+ Strings can be returned back to C from JavaScript, but one needs to be careful
+ about memory management. ::
+ EM_JS(const char*, get_unicode_str, (), {
+ var jsString = 'Hello with some exotic Unicode characters: Tässä on yksi lumiukko: ☃, ole hyvä.';
+ // 'jsString.length' would return the length of the string as UTF-16
+ // units, but Emscripten C strings operate as UTF-8.
+ var lengthBytes = lengthBytesUTF8(jsString)+1;
+ var stringOnWasmHeap = _malloc(lengthBytes);
+ stringToUTF8(jsString, stringOnWasmHeap, lengthBytes+1);
+ return stringOnWasmHeap;
+ });
+
+ int main() {
+ const char* str = get_unicode_str();
+ printf("UTF8 string says: %s\n", str);
+ // Each call to _malloc() must be paired with free(), or heap memory will leak!
+ free(str);
+ return 0;
+ }
+
.. c:macro:: EM_ASM(...)
Convenient syntax for inline assembly/JavaScript.
diff --git a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst
index 5df2811..ec987f2 100644
--- a/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst
+++ b/site/source/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.rst
@@ -21,6 +21,7 @@
- Call JavaScript functions from **C/C++**:
- :ref:`Using emscripten_run_script() <interacting-with-code-call-javascript-from-native>`.
+ - :ref:`Using EM_JS() <interacting-with-code-call-javascript-from-native>` (faster).
- :ref:`Using EM_ASM() <interacting-with-code-call-javascript-from-native>` (faster).
- :ref:`Using a C API implemented in JavaScript <implement-c-in-javascript>`.
- :ref:`As function pointers from C <interacting-with-code-call-function-pointers-from-c>`.
@@ -257,9 +258,30 @@
A faster way to call JavaScript from C is to write "inline JavaScript",
-using :c:func:`EM_ASM` (and related macros). These are used in a similar
-manner to inline assembly code. The "alert" example above might be
-written using inline JavaScript as:
+using :c:func:`EM_JS` or :c:func:`EM_ASM` (and related macros).
+
+EM_JS is used to declare JavaScript functions from inside a C file. The "alert"
+example might be written using EM_JS like:
+
+.. code-block:: c++
+
+ #include <emscripten.h>
+
+ EM_JS(void, call_alert, (), {
+ alert('hello world!');
+ throw 'all done';
+ });
+
+ int main() {
+ call_alert();
+ return 0;
+ }
+
+EM_JS's implementation is essentially a shorthand for :ref:`implementing a
+JavaScript library<implement-c-in-javascript>`.
+
+EM_ASM is used in a similar manner to inline assembly code. The "alert" example
+might be written with inline JavaScript as:
.. code-block:: c++
diff --git a/tests/core/test_em_js.cpp b/tests/core/test_em_js.cpp
index 5590221..8d8cde4 100644
--- a/tests/core/test_em_js.cpp
+++ b/tests/core/test_em_js.cpp
@@ -41,6 +41,14 @@
return 15;
});
+EM_JS(const char*, return_str, (), {
+ var jsString = 'hello from js';
+ var lengthBytes = jsString.length+1;
+ var stringOnWasmHeap = _malloc(lengthBytes);
+ stringToUTF8(jsString, stringOnWasmHeap, lengthBytes+1);
+ return stringOnWasmHeap;
+});
+
int main() {
printf("BEGIN\n");
noarg();
@@ -57,6 +65,8 @@
printf(" add_outer returned: %f\n", add_outer(5.5, 7.0, 14.375));
printf(" user_separator returned: %d\n", user_separator());
+ printf(" return_str returned: %s\n", return_str());
+
printf("END\n");
return 0;
}
diff --git a/tests/core/test_em_js.out b/tests/core/test_em_js.out
index 253c68c..f35c436 100644
--- a/tests/core/test_em_js.out
+++ b/tests/core/test_em_js.out
@@ -20,4 +20,5 @@
add_outer returned: 19.875000
can use <::> separator in user code
user_separator returned: 15
+ return_str returned: hello from js
END