diff --git a/Makefile.dep b/Makefile.dep index 3323dbb0269a..b283efb26bb3 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -715,6 +715,14 @@ USEPKG += nanocoap USEMODULE += gnrc_sock_udp endif +ifneq (,$(filter js,$(USEMODULE))) + USEPKG += jerryscript + USEMODULE += xtimer + USEMODULE += event + USEMODULE += saul_reg + USEMODULE += saul_default +endif + # always select gpio (until explicit dependencies are sorted out) FEATURES_OPTIONAL += periph_gpio diff --git a/examples/javascript/Makefile b/examples/javascript/Makefile index aaaa10be340a..c2f35f634561 100644 --- a/examples/javascript/Makefile +++ b/examples/javascript/Makefile @@ -24,8 +24,7 @@ CFLAGS += -DDEVELHELP # Set stack size to something (conservatively) enormous CFLAGS += -DTHREAD_STACKSIZE_MAIN=9092 -# Add the package for Jerryscript -USEPKG += jerryscript +USEMODULE += js include $(CURDIR)/../../Makefile.include diff --git a/examples/javascript/lib.js b/examples/javascript/lib.js new file mode 100644 index 000000000000..d8ba7f0b2f2d --- /dev/null +++ b/examples/javascript/lib.js @@ -0,0 +1,117 @@ +print("Hello from lib.js"); + +timer.setInterval = function (callback, interval) { + var next = timer.now() + interval; + var interval_handler = function () { + var res = callback(); + if (res != false) { + now = timer.now(); + while (next < now) { + next += interval; + } + next_interval = next - now; + timer.setTimeout(interval_handler, next_interval); + } + } + + return timer.setTimeout(interval_handler, interval); +} + +saul.type = { + CLASS_UNDEF : 0x00, /**< device class undefined */ + ACT_ANY : 0x40, /**< any actuator - wildcard */ + ACT_LED_RGB : 0x42, /**< actuator: RGB LED */ + ACT_SERVO : 0x43, /**< actuator: servo motor */ + ACT_MOTOR : 0x44, /**< actuator: motor */ + ACT_SWITCH : 0x45, /**< actuator: simple on/off switch */ + ACT_DIMMER : 0x46, /**< actuator: dimmable switch */ + SENSE_ANY : 0x80, /**< any sensor - wildcard */ + SENSE_BTN : 0x81, /**< sensor: simple button */ + SENSE_TEMP : 0x82, /**< sensor: temperature */ + SENSE_HUM : 0x83, /**< sensor: humidity */ + SENSE_LIGHT : 0x84, /**< sensor: light */ + SENSE_ACCEL : 0x85, /**< sensor: accelerometer */ + SENSE_MAG : 0x86, /**< sensor: magnetometer */ + SENSE_GYRO : 0x87, /**< sensor: gyroscope */ + SENSE_COLOR : 0x88, /**< sensor: (light) color */ + SENSE_PRESS : 0x89, /**< sensor: pressure */ + SENSE_ANALOG : 0x8a, /**< sensor: raw analog value */ + SENSE_UV : 0x8b, /**< sensor: UV index */ + CLASS_ANY : 0xff /**< any device - wildcard */ +} + +saul.op = { + LT : 0, + LE : 1, + EQ : 2, + GE : 3, + GT : 4 +} + +saul.set_methods = function(saul_object) { + if (typeof saul_object !== 'undefined') { + saul_object.on_threshold = function(threshold, callback, operator) { + var sensor = this; + var operator = (typeof operator !== 'undefined') ? operator : saul.op.GE; + var wait_flank = true; + + var poller = function() { + var val = sensor.read(); + var trigger = false; + switch (operator) { + case saul.op.LT: + trigger = (val < threshold); + break; + case saul.op.LE: + trigger = (val <= threshold); + break; + case saul.op.EQ: + trigger = (val == threshold); + break; + case saul.op.GE: + trigger = (val >= threshold); + break; + case saul.op.GT: + trigger = (val > threshold); + break; + } + + if (trigger && wait_flank) { + wait_flank = false; + return callback(); + } else { + if (! (trigger || wait_flank)) { + wait_flank = true; + } + return true; + } + } + + return timer.setInterval(poller, 100000); + } + saul_object.sample = function(n) { + var result = { + min : Number.MAX_VALUE, + max : Number.MIN_VALUE, + avg : 0 + } + + var left = n + while (left > 0) { + var val = this.read(); + result.avg += val; + result.max = Math.max(result.max, val); + result.min = Math.min(result.min, val); + left -= 1; + } + result.avg /= n; + return result; + } + } +} + +saul.get_one = function(type) { + var res = saul._get_one(type); + saul.set_methods(res); + return res; +} diff --git a/examples/javascript/main.c b/examples/javascript/main.c index 18d7523c9ff9..4d463d9fc0e7 100644 --- a/examples/javascript/main.c +++ b/examples/javascript/main.c @@ -23,53 +23,33 @@ #include #include -#include "jerryscript.h" -#include "jerryscript-ext/handler.h" +#include "js.h" -/* include header generated from main.js */ +/* include headers generated from *.js */ +#include "lib.js.h" #include "main.js.h" -int js_run(const jerry_char_t *script, size_t script_size) -{ - jerry_value_t ret_value; - - /* Initialize engine */ - jerry_init(JERRY_INIT_EMPTY); - - /* Register the print function in the global object. */ - jerryx_handler_register_global((const jerry_char_t *) "print", jerryx_handler_print); - - /* Setup Global scope code */ - ret_value = jerry_parse(script, script_size, false); - - if (!jerry_value_has_error_flag(ret_value)) { - /* Execute the parsed source code in the Global scope */ - ret_value = jerry_run(ret_value); - } - - int res = 0; - - if (jerry_value_has_error_flag(ret_value)) { - printf("js_run(): Script Error!"); - res = -1; - } - - jerry_release_value(ret_value); - - /* Cleanup engine */ - jerry_cleanup(); - - return res; -} +static event_queue_t event_queue; int main(void) { printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD); printf("This board features a(n) %s MCU.\n", RIOT_MCU); - printf("Executing main.js:\n"); + event_queue_init(&event_queue); + puts("Initializing jerryscript..."); + js_event_queue = &event_queue; + js_init(); + + puts("Executing lib.js..."); + js_run(lib_js, lib_js_len); + + puts("Executing main.js..."); js_run(main_js, main_js_len); + puts("Entering event loop..."); + event_loop(&event_queue); + return 0; } diff --git a/examples/javascript/main.js b/examples/javascript/main.js index 0243cda17745..9dd236f356e8 100644 --- a/examples/javascript/main.js +++ b/examples/javascript/main.js @@ -1 +1,12 @@ -print("Hello from JerryScript!"); +print("Hello from JerryScript!!!!"); + +//var n = 0; +//timer.setInterval(function () { n += 1; print(n, "triggers, time is", timer.now()); }, 1000000); + +button = saul.get_one(saul.type.SENSE_BTN); +led = saul.get_one(saul.type.ACT_SWITCH); + +led.write(1); + +button.on_threshold(0, function(){led.write(0); return true;}, saul.op.EQ); +button.on_threshold(1, function(){led.write(1); return true;}); diff --git a/sys/include/js.h b/sys/include/js.h new file mode 100644 index 000000000000..c10333b87305 --- /dev/null +++ b/sys/include/js.h @@ -0,0 +1,63 @@ +#ifndef JS_H +#define JS_H + +#include "clist.h" +#include "event.h" +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +extern event_queue_t *js_event_queue; + +typedef struct { + const char *name; + jerry_external_handler_t handler; +} js_native_method_t; + +typedef struct { + const char *name; + const js_native_method_t *methods; +} js_native_objects_t; + +typedef struct { + list_node_t ref; + jerry_value_t object; +} js_native_ref_t; + +typedef struct { + event_t event; + js_native_ref_t callback; +} js_callback_t; + +void js_init(void); +int js_run(const jerry_char_t *script, size_t script_size); + +/* internal */ +void js_native_ref_add(js_native_ref_t *ref, jerry_value_t object); +void js_native_ref_rem(js_native_ref_t *ref); +void js_init_objects(void); +void js_add_external_handler(jerry_value_t object, const char *name, jerry_external_handler_t handler); +void js_add_object(jerry_value_t object, jerry_value_t other, const char *name); +void *js_get_object_native_pointer(jerry_value_t object, const jerry_object_native_info_t *type); +jerry_value_t js_object_create_with_methods(const js_native_method_t *methods, unsigned num_methods); +jerry_value_t js_object_native_create(size_t size, const jerry_object_native_info_t *native_obj_type_info); +void js_callback_isr(void *arg); +void js_call_function(jerry_value_t callback); +void js_callback_init(js_callback_t *js_callback, jerry_value_t callback); +void js_callback_run(js_callback_t *js_callback); +void js_callback_cancel(js_callback_t *callback); +void js_shutdown(event_t *shutdown_done_event); +char *js_strdup(jerry_value_t string); + +double js_object_get_number(jerry_value_t object, const char *name); +jerry_value_t js_get_property(jerry_value_t object, const char *name); + +#define js_check(x) if (jerry_value_has_error_flag(x)) printf("%s:%u: js_check("#x") failed!\n", __FILE__, __LINE__) + +#define JS_EXTERNAL_HANDLER(name) \ + jerry_value_t \ + js_external_handler_ ## name (const jerry_value_t func_value, \ + const jerry_value_t this_value, \ + const jerry_value_t * args_p, \ + const jerry_length_t args_cnt) + +#endif /* JS_H */ diff --git a/sys/js/Makefile b/sys/js/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/js/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/js/callback.c b/sys/js/callback.c new file mode 100644 index 000000000000..765c4354b6b5 --- /dev/null +++ b/sys/js/callback.c @@ -0,0 +1,48 @@ +#include "js.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +void js_callback_isr(void *arg) +{ + js_callback_t *js_callback = (js_callback_t *)arg; + DEBUG("trigger %p\n", js_callback); + event_post(js_event_queue, &js_callback->event); +} + +static void js_event_callback(event_t *event) +{ + js_callback_t *js_callback = (js_callback_t *) event; + js_callback_run(js_callback); +} + +void js_callback_init(js_callback_t *js_callback, jerry_value_t callback) +{ + DEBUG("init %p\n", js_callback); + js_callback->event.handler = js_event_callback; + + DEBUG("acquire obj %lu\n", js_callback->callback.object); + js_native_ref_add(&js_callback->callback, callback); +} + +static void js_callback_cleanup(js_callback_t *js_callback) +{ + DEBUG("release %lu\n", callback); + js_native_ref_rem(&js_callback->callback); +} + +void js_callback_run(js_callback_t *js_callback) +{ + DEBUG("run %p\n", js_callback); + jerry_value_t callback = js_callback->callback.object; + js_call_function(callback); + js_callback_cleanup(js_callback); +} + +void js_callback_cancel(js_callback_t *js_callback) +{ + DEBUG("cancel %p\n", js_callback); + event_cancel(js_event_queue, &js_callback->event); + js_callback_cleanup(js_callback); +} + diff --git a/sys/js/init.c b/sys/js/init.c new file mode 100644 index 000000000000..f4227fa816e5 --- /dev/null +++ b/sys/js/init.c @@ -0,0 +1,30 @@ +#include "event.h" +#include "js.h" + +extern const js_native_method_t riot_methods[]; +extern const unsigned riot_methods_len; + +extern const js_native_method_t timer_methods[]; +extern const unsigned timer_methods_len; + +extern const js_native_method_t saul_methods[]; +extern const unsigned saul_methods_len; + +void js_init_objects(void) +{ + jerry_value_t global_object = jerry_get_global_object(); + + jerry_value_t riot_object = js_object_create_with_methods(riot_methods, riot_methods_len); + js_add_object(global_object, riot_object, "riot"); + jerry_release_value(riot_object); + + jerry_value_t timer_object = js_object_create_with_methods(timer_methods, timer_methods_len); + js_add_object(global_object, timer_object, "timer"); + jerry_release_value(timer_object); + + jerry_value_t saul_object = js_object_create_with_methods(saul_methods, saul_methods_len); + js_add_object(global_object, saul_object, "saul"); + jerry_release_value(saul_object); + + jerry_release_value(global_object); +} diff --git a/sys/js/js.c b/sys/js/js.c new file mode 100644 index 000000000000..c67271deb181 --- /dev/null +++ b/sys/js/js.c @@ -0,0 +1,223 @@ +#include +#include +#include + +#include "js.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +event_queue_t *js_event_queue; +static list_node_t js_native_refs; + +void js_add_external_handler(jerry_value_t object, const char *name, jerry_external_handler_t handler) +{ + /* Create a JS function object and wrap into a jerry value */ + jerry_value_t func_obj = jerry_create_external_function(handler); + js_check(func_obj); + + /* Set the native function as a property of the empty JS object */ + jerry_value_t prop_name = jerry_create_string((const jerry_char_t *) name); + js_check(prop_name); + + jerry_value_t set_prop = jerry_set_property(object, prop_name, func_obj); + js_check(set_prop); + + jerry_release_value(set_prop); + jerry_release_value(prop_name); + jerry_release_value(func_obj); +} + +void js_add_object(jerry_value_t object, jerry_value_t other, const char *name) +{ + jerry_value_t prop_name = jerry_create_string((const jerry_char_t *) name); + js_check(prop_name); + + jerry_value_t set_prop = jerry_set_property(object, prop_name, other); + js_check(set_prop); + + jerry_release_value(set_prop); + jerry_release_value(prop_name); +} + +jerry_value_t js_get_property(jerry_value_t object, const char *name) +{ + jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) name); + jerry_value_t prop_value = jerry_get_property (object, prop_name); + jerry_release_value (prop_name); + return prop_value; +} + +double js_object_get_number(jerry_value_t object, const char *name) +{ + double res; + jerry_value_t prop_value = js_get_property(object, name); + if (jerry_value_is_number(prop_value)) { + res = jerry_get_number_value(prop_value); + } + else { + res = 0; + } + + jerry_release_value(prop_value); + + return res; +} + +jerry_value_t js_object_native_create(size_t size, const jerry_object_native_info_t *native_obj_type_info) +{ + jerry_value_t object = jerry_create_object(); + + if (size) { + void *native_obj = malloc(size); + if (!native_obj) { + printf("%s(): malloc failed\n", __func__); + } + jerry_set_object_native_pointer(object, native_obj, native_obj_type_info); + } + + return object; +} + +void *js_get_object_native_pointer(jerry_value_t object, const jerry_object_native_info_t *type) +{ + void *result; + const jerry_object_native_info_t *type_tmp; + + bool has_p = jerry_get_object_native_pointer(object, &result, &type_tmp); + + return (has_p && (type_tmp == type)) ? result : NULL; +} + +jerry_value_t js_object_create_with_methods(const js_native_method_t *methods, unsigned num_methods) +{ + jerry_value_t object = jerry_create_object(); + + for (unsigned n = 0; n < num_methods; n++) { + const js_native_method_t *method = methods++; + js_add_external_handler(object, method->name, method->handler); + } + + return object; +} + +void js_call_function(jerry_value_t function) +{ + if (jerry_value_is_function(function)) { + jerry_value_t this_val = jerry_create_undefined(); + jerry_value_t ret_val = jerry_call_function(function, this_val, NULL, 0); + + if (!jerry_value_has_error_flag(ret_val)) { + DEBUG("%s():l%u %s\n", __FILE__, __LINE__, __func__); + } + else { + printf("%s():l%u %s\n", __FILE__, __LINE__, __func__); + } + + jerry_release_value(ret_val); + jerry_release_value(this_val); + } + else { + printf("%s():l%u %s\n", __FILE__, __LINE__, __func__); + } +} + +int js_run(const jerry_char_t *script, size_t script_size) +{ + jerry_value_t ret_value; + + /* Setup Global scope code */ + ret_value = jerry_parse(script, script_size, false); + + if (!jerry_value_has_error_flag(ret_value)) { + /* Execute the parsed source code in the Global scope */ + ret_value = jerry_run(ret_value); + } + + int res = 0; + + if (jerry_value_has_error_flag(ret_value)) { + puts("js_run(): Script Error!"); + res = -1; + } + + jerry_release_value(ret_value); + + return res; +} + +void js_init(void) +{ + assert(js_event_queue); + + /* Initialize engine */ + jerry_init(JERRY_INIT_EMPTY); + + /* Register the print function in the global object. */ + jerryx_handler_register_global((const jerry_char_t *) "print", jerryx_handler_print); + + js_init_objects(); +} + +static event_t *js_shutdown_done_event; + +static void js_shutdown_event_handler(event_t *event) +{ + (void)event; + + unsigned state = irq_disable(); + + /* drain all js callbackevents from queue */ + while (event_get(js_event_queue)); + + /* un-reference all jerryscript objects referenced by native code */ + js_native_ref_t *ref; + while ((ref = (js_native_ref_t*) clist_lpop(&js_native_refs))) { + jerry_release_value(ref->object); + } + + /* cleanup jerryscript engine */ + jerry_cleanup(); + + irq_restore(state); + + if (js_shutdown_done_event) { + event_post(js_event_queue, js_shutdown_done_event); + } +} + +static event_t js_shutdown_event = { .handler = js_shutdown_event_handler }; + +void js_shutdown(event_t *done_event) +{ + js_shutdown_done_event = done_event; + event_post(js_event_queue, &js_shutdown_event); +} + +char *js_strdup(jerry_value_t string) +{ + size_t len = jerry_get_string_length(string); + if (!len) { + return NULL; + } + + char *buf = malloc(len+1); + if (buf) { + buf[jerry_string_to_char_buffer(string, (uint8_t*)buf, len)] = '\0'; + } + return buf; +} + +void js_native_ref_add(js_native_ref_t *ref, jerry_value_t object) +{ + ref->object = jerry_acquire_value(object); + clist_rpush(&js_native_refs, &ref->ref); +} + +void js_native_ref_rem(js_native_ref_t *ref) +{ + /* TODO: unfortunately, this is O(n) */ + if (clist_remove(&js_native_refs, &ref->ref)) { + jerry_release_value(ref->object); + } +} diff --git a/sys/js/riot.c b/sys/js/riot.c new file mode 100644 index 000000000000..df235b17c000 --- /dev/null +++ b/sys/js/riot.c @@ -0,0 +1,19 @@ +#include "js.h" +#include "periph/pm.h" + +static JS_EXTERNAL_HANDLER(reboot) { + (void)func_value; + (void)this_value; + (void)args_p; + (void)args_cnt; + + pm_reboot(); + + return 0; +} + +const js_native_method_t riot_methods[] = { + { "reboot", js_external_handler_reboot } +}; + +const unsigned riot_methods_len = sizeof(riot_methods)/sizeof(riot_methods[0]); diff --git a/sys/js/saul.c b/sys/js/saul.c new file mode 100644 index 000000000000..0d4945f09253 --- /dev/null +++ b/sys/js/saul.c @@ -0,0 +1,155 @@ +#include +#include +#include + +#include "event.h" +#include "js.h" +#include "saul_reg.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +static void _js_saul_reg_freecb(void *native_p) +{ + (void)native_p; + DEBUG("%s:l%u:%s()\n", __FILE__, __LINE__, __func__); +} + +static const jerry_object_native_info_t js_saul_reg_object_native_info = +{ + .free_cb = _js_saul_reg_freecb +}; + +static JS_EXTERNAL_HANDLER(saul_reg_read) +{ + (void)func_value; +// (void)this_value; + (void)args_p; + (void)args_cnt; + + phydat_t phydat; + saul_reg_t *saul_reg = js_get_object_native_pointer(this_value, &js_saul_reg_object_native_info); + if (!saul_reg) { + puts("nosaulreg"); + goto error; + } + int res = saul_reg_read(saul_reg, &phydat); + if (res < 1) { + puts("saulnothingread"); + goto error; + } + return jerry_create_number(phydat.val[0]); + +error: + return jerry_create_undefined(); +} + +static JS_EXTERNAL_HANDLER(saul_reg_write) +{ + (void)func_value; +// (void)this_value; + (void)args_p; + (void)args_cnt; + + saul_reg_t *saul_reg = js_get_object_native_pointer(this_value, &js_saul_reg_object_native_info); + if (!saul_reg) { + puts("nosaulreg"); + goto error; + } + if (args_cnt < 1) { + puts("saul_write(): not enough arguments"); + goto error; + } + + phydat_t phydat; + memset(&phydat, '\0', sizeof(phydat)); + + for (unsigned i = 0; i < args_cnt; i++) { + if (jerry_value_is_number(args_p[i])) { + phydat.val[i] = jerry_get_number_value(args_p[i]); + } else { + printf("saul_write(): arg %i is not a number!", i); + goto error; + } + } + + saul_reg_write(saul_reg, &phydat); + +error: + return jerry_create_undefined(); +} + +const js_native_method_t saul_reg_methods[] = { + { "read", js_external_handler_saul_reg_read }, + { "write", js_external_handler_saul_reg_write }, +}; + +const unsigned saul_reg_methods_len = sizeof(saul_reg_methods) / sizeof(saul_reg_methods[0]); + +static jerry_value_t js_saul_reg_create(saul_reg_t *saul_reg) +{ + jerry_value_t object = js_object_create_with_methods(saul_reg_methods, saul_reg_methods_len); + jerry_set_object_native_pointer(object, saul_reg, &js_saul_reg_object_native_info); + return object; +} + +static JS_EXTERNAL_HANDLER(saul_get_one) { + (void)func_value; + (void)this_value; + + if (args_cnt < 1) { + puts("saul.get_one(): not enough arguments"); + return 0; + } + + if (!jerry_value_is_number(args_p[0])) { + puts("saul.get_one(): arg 0 not a saul type"); + return 0; + } + +/* int pos = 0; + if ((args_cnt) > 1 && jerry_value_is_number(args_p[1])) { + pos = jerry_get_number_value(args_p[1]); + } +*/ + + saul_reg_t *entry = saul_reg_find_type(jerry_get_number_value(args_p[0])); + if (entry) { + return js_saul_reg_create(entry); + } else { + return jerry_create_undefined(); + } +} + +static JS_EXTERNAL_HANDLER(saul_find_name) { + (void)func_value; + (void)this_value; + + if (args_cnt < 1) { + puts("saul.get_by_name(): not enough arguments"); + return 0; + } + + if (!jerry_value_is_string(args_p[0])) { + puts("saul.get_by_name(): arg 0 not a string"); + return 0; + } + + char name[32]; + memset(name, '\0', sizeof(name)); + jerry_string_to_char_buffer(args_p[0], (jerry_char_t*)name, sizeof(name)); + saul_reg_t *entry = saul_reg_find_name(name); + if (entry) { + DEBUG("saul_find_name(): found entry for name \"%s\"\n", name); + return js_saul_reg_create(entry); + } else { + DEBUG("saul_find_name(): no entry found for name \"%s\"\n", name); + return jerry_create_undefined(); + } +} +const js_native_method_t saul_methods[] = { + { "_get_one", js_external_handler_saul_get_one }, + { "_find_name", js_external_handler_saul_find_name } +}; + +const unsigned saul_methods_len = sizeof(saul_methods) / sizeof(saul_methods[0]); diff --git a/sys/js/timer.c b/sys/js/timer.c new file mode 100644 index 000000000000..cf116c5dea16 --- /dev/null +++ b/sys/js/timer.c @@ -0,0 +1,89 @@ +#include +#include +#include + +#include "event.h" +#include "js.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +typedef struct { + js_callback_t callback; + jerry_value_t object; + xtimer_t xtimer; +} js_xtimer_t; + +static void _js_xtimer_freecb(void *native_p) +{ + DEBUG("%s:l%u:%s()\n", __FILE__, __LINE__, __func__); + + js_xtimer_t *js_xtimer = (js_xtimer_t *) native_p; + xtimer_remove(&js_xtimer->xtimer); + free(js_xtimer); +} + +static const jerry_object_native_info_t js_xtimer_object_native_info = +{ + .free_cb = _js_xtimer_freecb +}; + +static jerry_value_t js_xtimer_create(jerry_value_t callback, uint32_t timeout) +{ + jerry_value_t object = js_object_native_create(sizeof(js_xtimer_t), &js_xtimer_object_native_info); + js_xtimer_t *js_xtimer = js_get_object_native_pointer(object, &js_xtimer_object_native_info); + + if (!js_xtimer) { + DEBUG("%s():l%u\n", __FILE__, __LINE__); + } + + /* add the xtimer object as property to the callback so + * it stays referenced */ + js_add_object(callback, object, "_obj"); + + memset(js_xtimer, '\0', sizeof(*js_xtimer)); + js_callback_init(&js_xtimer->callback, callback); + js_xtimer->xtimer.callback = js_callback_isr; + js_xtimer->xtimer.arg = js_xtimer; + xtimer_set(&js_xtimer->xtimer, timeout); + + return object; +} + +static JS_EXTERNAL_HANDLER(timer_setTimeout) { + (void)func_value; + (void)this_value; + + if (args_cnt < 2) { + puts("timer.setTimeout(): not enough arguments"); + return 0; + } + + if (!jerry_value_is_function(args_p[0])) { + puts("timer.setTimeout(): arg 0 not a function"); + return 0; + } + if (!jerry_value_is_number(args_p[1])) { + puts("timer.setTimeout(): arg 1 not a number"); + return 0; + } + + return js_xtimer_create(args_p[0], jerry_get_number_value(args_p[1])); +} + +static JS_EXTERNAL_HANDLER(timer_now) { + (void)func_value; + (void)this_value; + (void)args_p; + (void)args_cnt; + + return jerry_create_number(xtimer_now_usec()); +} + +const js_native_method_t timer_methods[] = { + { "setTimeout", js_external_handler_timer_setTimeout }, + { "now", js_external_handler_timer_now } +}; + +const unsigned timer_methods_len = sizeof(timer_methods) / sizeof(timer_methods[0]);