Blog Forum Contact Us

low.js native API

Native modules written in C/C++ can be loaded by low.js programs, allowing the programs to be written in a mixture of JavaScript and C/C++. This functionality is currently available on:

The native modules are written with the low native API, which consists of

For all functions, see API reference below.

With the DukTape API and the additional functions the native module can not only define its interface for the calling JavaScript program, it can also call the JavaScript program and access all JavaScript libraries.

Example

A very simple example can be found at GitHub.

Walkthrough of this example:

Important notes on the architecture

Linker script / Memory model

Native modules have to be linked with the settings and the linker script with is already used in the Makefile of the supplied example. Please copy these into your own Makefile / build environment.

On the ESP32, the native modules run from Flash. IRAM is not used.

DukTape / low.js context

low_context, defined in low.h, is a typedef of duk_context, and thus means the same thing. There is only one duk_context/low_context pointer for a given program.

The additional low.js functions can often be called from any thread, however, while the DukTape functions may only be called from the code thread.

Module loading / unloading functions

Defined in low.h

__attribute__ ((visibility ("default"))) bool module_load(duk_context *ctx, const char *module_path);

When a module is loaded, the function module_load is called from JavaScript. On the DukTape stack the module object and the exports object of the module is pushed. What this function does is equivalent to the global scope code in JavaScript modules.

As module_load is called from JavaScript, you may throw JavaScript (DukTape) and C++ exceptions.

__attribute__ ((visibility ("default"))) bool module_unload();

When a module is unloaded, the function module_unload is called. This happens when the user program is exiting. DukTape is already deinitialized and all your finalizers were called. Here you must deallocate all memory allocated in the module.

Failure of doing this will result into memory leaks, as on ESP32 the microcontroller is not reset between runs of the user program.

Exceptions

DukTape is compiled with C++ exceptions instead of longjmp/setjmp, because this is far more efficient on the ESP32: Every setjmp requires lots of stack because of the many, many registers and also requires lots of CPU, C++ exceptions are far more lean on the stack and only use CPU when an exception occures.

So, naturally, low.js native modules also support C++ exceptions.

DukTape and the additional low.js functions may throw, so you should always be aware of this. Do not allocate dynamic memory which could be leaked if your code gets interrupted. You can use duk_push_fixed_buffer to allocate memory which is referenced by the DukTape stack and keep the memory after end of the call by referencing the memory from the stash with low_add_stash. Or, you can use C++ style try/catch to free dynamically memory in case of a throw. If you do this, use catch(…) to catch DukTape/JavaScript errors and always rethrow these, otherwise unexpected behavior will happen!

In functions called by JavaScript code, you can of course throw std::exceptions, such as std::bad_alloc, however this will result in cryptic exception messages in JavaScript. Better practice is to throw Node.JS errors including error code with low_push_error and duk_throw, or at least a JavaScript error with the DukTape error functions.

When allocating memory with C++, you can use low_alloc_throw (or operator new(duk_context *), defined inline in low.h, which uses low_alloc_throw), so a proper Node.JS error is thrown.

Threads

low.js uses at least three threads.

The code thread
This is where the JavaScript codes runs. You must only use DukTape in this thread. module_load and module_unload are called from this thread.

The worker thread
This is where work is done which should run asyncronly. For example, all file operations which are scheduled are done in this thread. On the PC, multiple worker threads may exist, so multiple scheduled operations can run at the same time.

The immediate thread
In this thread, only things should be done, which do not block. For example, HTTP/SSL operations happen here.

How to implement asynchronous functions with callback

In your function which JavaScript calls, validate the parameters. On error, schedule a call to the user callback function with low_call_next_tick. If everything is OK, save the user callback with low_add_stash and schedule a call to the worker thread with low_call_thread.

In that call, do you work and schedule a call back to the code thread with low_call_thread.

In that call in the code thread, get the user callback from stash with low_push_stash and call it with duk_call. Note that duk_call may throw through your code if the JavaScript program throws, so clean up all dynamic memory and so on before the call to duk_call.

API Reference

The low.js specific functions

Defined in low.h

void low_load_module(duk_context *ctx, const char *path, bool parent_on_stack);

require() a module. If you have access to your module object (put on stash with low_add_stash in module_load for example), put it on the top of your stack before calling low_load_module and call it with parent_on_stack set to true. If you do not do this, the module’s parent will not be set correctly (which might be fine most of the time).

void low_call_thread(duk_context *ctx, low_thread thread, int priority, void (*func)(low_context *ctx, void *userdata), void *userdata);

The given callback will be called with the given user data from the given thread. As thread, you may give:

With LOW_THREAD_WORKER, you can give different priorities. Use 0 for faster workers, 1 for slower workers. As a reference: low.js itself uses 0 for reading files, 1 for writing files, which for example keeps the neonious IDE more responsive even when saving files.

low_thread low_get_current_thread(duk_context *ctx);

Returns the thread you are calling this function from. Return value is one of:

int low_set_timeout(duk_context *ctx, int index, int delay, void (*call)(low_context *ctx, void *userdata), void *data);

Must only be called from the code thread!

Schedules a callback to be called with the given user data after the delay specified in milliseconds. If this shall be a new timeout, set index to 0, otherwise set index to the value returned by the previous low_set_timeout. This function will return the index of the scheduled call, which is never 0 and equal to the index given if it was not 0 (which means schedule a new call).

void low_clear_timeout(duk_context *ctx, int index);

Must only be called from the code thread!

Clears a callback call scheduled with low_set_timeout.

void low_call_next_tick(duk_context *ctx, int num_args);

Must only be called from the code thread!

A drop in replacement to duk_call which schedules the call to happen in the next tick. The equivalent to process.nextTick in JavaScript. Note that this function removes the function and the arguments from the DukTape stack but does NOT add a return code to the stack as duk_call does.

int low_add_stash(duk_context *ctx, int index);

Must only be called from the code thread!

Adds a variable from the DukTape stack to the stash, saving it for future use even after the current call ends. Useful to save callbacks.

With the variable on the stash, you can add it back to the DukTape stack by calling low_push_stash with the return value of low_add_stash later.

void low_remove_stash(duk_context *ctx, int index);

Must only be called from the code thread!

Removes a variable from the DukTape stack.

void low_push_stash(duk_context *ctx, int index, bool remove);

Pushes a variable from the stash to the DukTape stack. If the function is called with remove set to true, the variable will be removed from the stash.

void *low_push_buffer(duk_context *ctx, int len);

Must only be called from the code thread!

Creates a new buffer and pushes it on the DukTape stack. The buffer has all properties of a regular Node.JS Buffer.

This function call returns a pointer to the data of the buffer.

This function throws if memory is full.

void low_push_error(duk_context *ctx, int error, const char *syscall);

Must only be called from the code thread!

Creates a Node.JS type error with error code and error message. Error is the errno which happened, syscall is the name of the function which resulted in the error.

void *low_alloc_throw(duk_context *ctx, size_t size);

Must only be called from the code thread!

Same as malloc, but throws an DukTape error if memory is full.

Available functions of DukTape API

Please note that duk_require_... functions are not exported so errors have to be actively handled.

duk_api_global_filename
duk_api_global_line
duk_base64_decode
duk_base64_encode
duk_buffer_to_string
duk_call
duk_call_method
duk_call_prop
duk_char_code_at
duk_check_type
duk_check_type_mask
duk_components_to_time
duk_concat
duk_copy
duk_decode_string
duk_def_prop
duk_del_prop
duk_del_prop_index
duk_del_prop_literal_raw
duk_del_prop_lstring
duk_del_prop_string
duk_dup
duk_dup_top
duk_enum
duk_equals
duk_error_raw
duk_freeze
duk_generic_error_stash
duk_get_boolean
duk_get_boolean_default
duk_get_buffer_data
duk_get_buffer_data_default
duk_get_c_function
duk_get_c_function_default
duk_get_finalizer
duk_get_int
duk_get_int_default
duk_get_length
duk_get_lstring
duk_get_lstring_default
duk_get_now
duk_get_number
duk_get_number_default
duk_get_pointer
duk_get_pointer_default
duk_get_prop
duk_get_prop_desc
duk_get_prop_index
duk_get_prop_literal_raw
duk_get_prop_lstring
duk_get_prop_string
duk_get_prototype
duk_get_string
duk_get_string_default
duk_get_top
duk_get_top_index
duk_get_type
duk_get_type_mask
duk_get_uint
duk_get_uint_default
duk_has_prop
duk_has_prop_index
duk_has_prop_literal_raw
duk_has_prop_lstring
duk_has_prop_string
duk_hex_decode
duk_hex_encode
duk_insert
duk_instanceof
duk_is_array
duk_is_boolean
duk_is_buffer_data
duk_is_c_function
duk_is_constructable
duk_is_constructor_call
duk_is_function
duk_is_nan
duk_is_null
duk_is_number
duk_is_object
duk_is_pointer
duk_is_string
duk_is_symbol
duk_is_undefined
duk_is_valid_index
duk_join
duk_json_decode
duk_json_encode
duk_map_string
duk_new
duk_next
duk_normalize_index
duk_pop
duk_pop_2
duk_pop_3
duk_pop_n
duk_push_array
duk_push_boolean
duk_push_c_function
duk_push_current_function
duk_push_error_object_raw
duk_push_false
duk_push_int
duk_push_literal_raw
duk_push_lstring
duk_push_nan
duk_push_new_target
duk_push_null
duk_push_number
duk_push_object
duk_push_pointer
duk_push_proxy
duk_push_sprintf
duk_push_string
duk_push_this
duk_push_true
duk_push_uint
duk_push_undefined
duk_put_function_list
duk_put_number_list
duk_put_prop
duk_put_prop_index
duk_put_prop_literal_raw
duk_put_prop_lstring
duk_put_prop_string
duk_random
duk_remove
duk_replace
duk_samevalue
duk_seal
duk_set_finalizer
duk_set_length
duk_set_prototype
duk_set_top
duk_strict_equals
duk_substring
duk_swap
duk_swap_top
duk_throw_raw
duk_time_to_components
duk_to_boolean
duk_to_int
duk_to_int32
duk_to_lstring
duk_to_null
duk_to_number
duk_to_object
duk_to_pointer
duk_to_primitive
duk_to_string
duk_to_uint
duk_to_uint16
duk_to_uint32
duk_to_undefined
duk_trim

Available functions of C POSIX on ESP32

This list will be extended over time and on request (ask in the Forum). On the PC all functions are available which can be dynamically loaded.

abs
acos
asin
atan
atan2
atoi
ceil
close
copysign
cos
difftime
div
environ
exp
fabs
fclose
ferror
fflush
fgets
finite
fiprintf
floor
fmod
fopen
fprintf
fputwc
fread
free
frexp
fseek
fseeko
fstat
fwrite
getenv
getpid
gettimeofday
gmtime_r
index
isalnum
isspace
iswspace
labs
localeconv
localtime_r
log
lseek
matherr
mbrtowc
memchr
memcmp
memcpy
memmove
memset
mktime
nan
nanf
open
pow
printf
putc
putchar
puts
qsort
rand
read
rename
rint
scalbn
setlocale
sin
sniprintf
snprintf
sprintf
sqrt
srand
sscanf
stat
strcasecmp
strchr
strcmp
strcpy
strdup
strerror
strftime
strlcat
strlcpy
strlen
strncasecmp
strncmp
strncpy
strnlen
strptime
strrchr
strstr
strtod
strtof
strtok_r
strtol
strtoul
tan
time
tolower
toupper
ungetc
unlink
vfiprintf
vfprintf
vprintf
vsnprintf
wcrtomb
write