Skip to content

Commit 9112ce0

Browse files
committed
Change chkargs. Support strings as keys in obj literals
1 parent cd9ac73 commit 9112ce0

File tree

5 files changed

+77
-92
lines changed

5 files changed

+77
-92
lines changed

README.md

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -239,32 +239,24 @@ char *js_getstr(struct js *js, jsval_t val, size_t *len); // Get string
239239

240240
Extract C values from JS values
241241

242-
### js\_checkargs()
242+
### js\_chkargs()
243243

244244
```c
245-
jsval_t js_checkargs(struct js *js, jsval_t *args, int nargs, const char *spec, ...);
245+
bool js_chkargs(jsval_t *args, int nargs, const char *spec);
246246
```
247247
248-
A helper function that fetches JS arguments into C values, according to
249-
`spec` type specification. Return `JS_ERR` on error, or `JS_UNDEF` on success.
250-
Supported specifiers:
251-
- `b` for `bool`
252-
- `d` for `double`
253-
- `i` for `char`, `short`, `int`, and corresponding unsigned variants
254-
- `s` for `char *`
255-
- `j` for `jsval_t`
248+
A helper function that checks a validity of the arguments passed to a function.
249+
A `spec` is a 0-terminated string where each character represents a type of
250+
the expected argument: `b` for `bool`, `d` for number, `s` for string, `j`
251+
for any other JS value.
256252
257253
Usage example - a C function that implements a JS function
258254
`greater_than(number1, number2)`:
259255
260256
```c
261-
jsval_t js_gt(struct js *js, jsval_t *args, int nargs) {
262-
double a, b;
263-
jsval_t res = js_checkargs(js, args, nargs, "dd", &a, &b);
264-
if (js_type(res) == JS_UNDEF) {
265-
res = a > b ? js_mktrue() : js_mkfalse();
266-
}
267-
return res;
257+
static jsval_t js_gt(struct js *js, jsval_t *args, int nargs) {
258+
if (!js_chkargs(args, nargs, "dd")) return js_mkerr(js, "bad args!");
259+
return js_getnum(args[0]) > js_getnum(args[1]) ? js_mktrue() : js_mkfalse();
268260
}
269261
```
270262

elk.c

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
#endif
2222

2323
#include <assert.h>
24-
#include <inttypes.h>
2524
#include <math.h>
2625
#include <stdarg.h>
27-
#include <stdbool.h>
2826
#include <stdio.h>
2927
#include <stdlib.h>
3028
#include <string.h>
@@ -866,14 +864,20 @@ static jsval_t js_obj_literal(struct js *js) {
866864
if (is_err(obj)) return obj;
867865
js->consumed = 1;
868866
while (next(js) != TOK_RBRACE) {
869-
EXPECT(TOK_IDENTIFIER, );
870-
size_t koff = js->toff, klen = js->tlen;
867+
jsval_t key = 0;
868+
if (js->tok == TOK_IDENTIFIER) {
869+
if (exe) key = js_mkstr(js, js->code + js->toff, js->tlen);
870+
} else if (js->tok == TOK_STRING) {
871+
if (exe) key = js_str_literal(js);
872+
} else {
873+
return js_mkerr(js, "parse error");
874+
}
875+
js->consumed = 1;
871876
EXPECT(TOK_COLON, );
872877
jsval_t val = js_expr(js);
873878
if (exe) {
874879
// printf("XXXX [%s] scope: %lu\n", js_str(js, val), vdata(js->scope));
875880
if (is_err(val)) return val;
876-
jsval_t key = js_mkstr(js, js->code + koff, klen);
877881
if (is_err(key)) return key;
878882
jsval_t res = setprop(js, obj, key, resolveprop(js, val));
879883
if (is_err(res)) return res;
@@ -1369,27 +1373,15 @@ void js_stats(struct js *js, size_t *total, size_t *lwm, size_t *css) {
13691373
}
13701374
// clang-format on
13711375

1372-
jsval_t js_checkargs(struct js *js, jsval_t *args, int nargs, const char *spec,
1373-
...) {
1374-
va_list ap;
1375-
int i = 0;
1376-
va_start(ap, spec);
1377-
for (i = 0; i < nargs; i++) {
1378-
uint8_t t = vtype(args[i]);
1379-
switch (spec[i]) { // clang-format off
1380-
case 'b': if (t != T_BOOL) goto fail; *(va_arg(ap, bool *)) = js_getbool(args[i]); break;
1381-
case 'd': if (t != T_NUM) goto fail; *(va_arg(ap, double *)) = js_getnum(args[i]); break;
1382-
case 'i': if (t != T_NUM) goto fail; *(va_arg(ap, int *)) = (int) js_getnum(args[i]); break;
1383-
case 's': if (t != T_STR) goto fail; *(va_arg(ap, char **)) = js_getstr(js, args[i], NULL); break;
1384-
case 'j': *(va_arg(ap, jsval_t *)) = args[i]; break;
1385-
default: goto fail;
1386-
} // clang-format on
1376+
bool js_chkargs(jsval_t *args, int nargs, const char *spec) {
1377+
int i = 0, ok = 1;
1378+
for (; ok && i < nargs && spec[i]; i++) {
1379+
uint8_t t = vtype(args[i]), c = (uint8_t) spec[i];
1380+
ok = (c == 'b' && t == T_BOOL) || (c == 'd' && t == T_NUM) ||
1381+
(c == 's' && t == T_STR) || (c == 'j');
13871382
}
1388-
va_end(ap);
1389-
if (spec[i] != '\0') return js_mkerr(js, "arg count");
1390-
return js_mkundef();
1391-
fail:
1392-
return js_mkerr(js, "arg %d", i);
1383+
if (spec[i] != '\0' || i != nargs) ok = 0;
1384+
return ok;
13931385
}
13941386

13951387
jsval_t js_eval(struct js *js, const char *buf, size_t len) {

elk.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define JS_VERSION "3.0.0"
1818
#pragma once
1919

20+
#include <stdbool.h>
2021
#include <stddef.h>
2122
#include <stdint.h>
2223

@@ -31,9 +32,9 @@ struct js *js_create(void *buf, size_t len); // Create JS instance
3132
jsval_t js_eval(struct js *, const char *, size_t); // Execute JS code
3233
jsval_t js_glob(struct js *); // Return global object
3334
const char *js_str(struct js *, jsval_t val); // Stringify JS value
34-
jsval_t js_checkargs(struct js *, jsval_t *, int, const char *, ...); // Check
35-
void js_setmaxcss(struct js *, size_t); // Set max C stack size
36-
void js_setgct(struct js *, size_t); // Set GC trigger threshold
35+
bool js_chkargs(jsval_t *, int, const char *); // Check args validity
36+
void js_setmaxcss(struct js *, size_t); // Set max C stack size
37+
void js_setgct(struct js *, size_t); // Set GC trigger threshold
3738
void js_stats(struct js *, size_t *total, size_t *min, size_t *cstacksize);
3839
void js_dump(struct js *); // Print debug info. Requires -DJS_DUMP
3940

examples/Esp32JS/JS.h

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,26 @@ static void logstats(void) {
5555
// These functions below will be imported into the JS engine.
5656
// Note that they are inside the extern "C" section.
5757
static jsval_t gpio_write(struct js *js, jsval_t *args, int nargs) {
58-
int pin, val;
59-
jsval_t res = js_checkargs(js, args, nargs, "ii", &pin, &val);
60-
if (js_type(res) == JS_UNDEF) {
61-
MG_INFO(("gpio.write %d -> %d", pin, val));
62-
digitalWrite(pin, val);
63-
}
64-
return res;
58+
if (!js_chkargs(args, nargs, "dd")) return js_mkerr(js, "bad args");
59+
int pin = js_getnum(args[0]), val = js_getnum(args[1]);
60+
MG_INFO(("gpio.write %d -> %d", pin, val));
61+
digitalWrite(pin, val);
62+
return js_mknull();
6563
}
6664

6765
static jsval_t gpio_read(struct js *js, jsval_t *args, int nargs) {
68-
int pin;
69-
jsval_t res = js_checkargs(js, args, nargs, "i", &pin);
70-
if (js_type(res) == JS_UNDEF) {
71-
MG_INFO(("gpio.read %d", pin));
72-
res = js_mknum(digitalRead(pin));
73-
}
74-
return res;
66+
if (!js_chkargs(args, nargs, "d")) return js_mkerr(js, "bad args");
67+
int pin = js_getnum(args[0]);
68+
MG_INFO(("gpio.read %d", pin));
69+
return js_mknum(digitalRead(pin));
7570
}
7671

7772
static jsval_t gpio_mode(struct js *js, jsval_t *args, int nargs) {
78-
int pin, mode;
79-
jsval_t res = js_checkargs(js, args, nargs, "ii", &pin, &mode);
80-
if (js_type(res) == JS_UNDEF) {
81-
MG_INFO(("gpio.mode %d -> %d", pin, mode));
82-
pinMode(pin, mode);
83-
}
84-
return res;
73+
if (!js_chkargs(args, nargs, "dd")) return js_mkerr(js, "bad args");
74+
int pin = js_getnum(args[0]), mode = js_getnum(args[1]);
75+
MG_INFO(("gpio.mode %d -> %d", pin, mode));
76+
pinMode(pin, mode);
77+
return js_mknull();
8578
}
8679

8780
void timer_cleanup(void *data) {
@@ -105,26 +98,20 @@ static void js_timer_fn(void *userdata) {
10598
}
10699

107100
static jsval_t mktimer(struct js *js, jsval_t *args, int nargs) {
108-
int milliseconds = 0;
109-
const char *funcname = NULL;
110-
jsval_t res = js_checkargs(js, args, nargs, "is", &milliseconds, &funcname);
111-
if (js_type(res) == JS_UNDEF) {
112-
struct mg_timer *t = mg_timer_add(&s_mgr, milliseconds, MG_TIMER_REPEAT,
113-
js_timer_fn, strdup(funcname));
114-
MG_INFO(("mktimer %lu, %d ms, fn %s", t->id, milliseconds, funcname));
115-
addresource(timer_cleanup, (void *) t->id);
116-
res = js_mknum(t->id);
117-
}
118-
return res;
101+
if (!js_chkargs(args, nargs, "ds")) return js_mkerr(js, "bad args");
102+
int milliseconds = js_getnum(args[0]);
103+
const char *funcname = js_getstr(js, args[1], NULL);
104+
struct mg_timer *t = mg_timer_add(&s_mgr, milliseconds, MG_TIMER_REPEAT,
105+
js_timer_fn, strdup(funcname));
106+
MG_INFO(("mktimer %lu, %d ms, fn %s", t->id, milliseconds, funcname));
107+
addresource(timer_cleanup, (void *) t->id);
108+
return js_mknum(t->id);
119109
}
120110

121111
static jsval_t deltimer(struct js *js, jsval_t *args, int nargs) {
122-
unsigned long id;
123-
jsval_t res = js_checkargs(js, args, nargs, "i", &id);
124-
if (js_type(res) == JS_UNDEF) {
125-
delresource(timer_cleanup, (void *) id);
126-
}
127-
return res;
112+
if (!js_chkargs(args, nargs, "d")) return js_mkerr(js, "bad args");
113+
delresource(timer_cleanup, (void *) (unsigned long) js_getnum(args[0]));
114+
return js_mknull();
128115
}
129116

130117
static jsval_t js_log(struct js *js, jsval_t *args, int nargs) {

test/unit_test.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ static void test_basic(void) {
126126
assert(ev(js, "a = {b:2}", "{\"b\":2}"));
127127
assert(ev(js, "a", "{\"b\":2}"));
128128
assert(ev(js, "a.b", "2"));
129+
assert(ev(js, "({a:3}).a", "3"));
130+
assert(ev(js, "({\"a\":4})", "{\"a\":4}"));
129131
assert(ev(js, "a.b = {c:3}", "{\"c\":3}"));
130132
assert(ev(js, "a", "{\"b\":{\"c\":3}}"));
131133
assert(ev(js, "a.b.c", "3"));
@@ -419,6 +421,12 @@ static void test_funcs(void) {
419421
assert(ev(js, "f() + 2;", "3"));
420422

421423
assert(ev(js, "f=function (x){return x+1;}; f(1);", "2"));
424+
425+
assert(ev(js, "f = function(x){return x;};", "function(x){return x;}"));
426+
assert(ev(js, "f(2)", "2"));
427+
assert(ev(js, "f({})", "{}"));
428+
assert(ev(js, "f({a:5,b:3}).b", "3"));
429+
assert(ev(js, "f({\"a\":5,\"b\":3}).b", "3"));
422430
}
423431

424432
static void test_bool(void) {
@@ -516,12 +524,8 @@ static jsval_t js_set_timer(struct js *js, jsval_t *args, int nargs) {
516524
}
517525

518526
static jsval_t js_gt(struct js *js, jsval_t *args, int nargs) {
519-
double a, b;
520-
jsval_t res = js_checkargs(js, args, nargs, "dd", &a, &b);
521-
if (js_type(res) == JS_UNDEF) {
522-
res = a > b ? js_mktrue() : js_mkfalse();
523-
}
524-
return res;
527+
if (!js_chkargs(args, nargs, "dd")) return js_mkerr(js, "doh");
528+
return js_getnum(args[0]) > js_getnum(args[1]) ? js_mktrue() : js_mkfalse();
525529
}
526530

527531
static void test_c_funcs(void) {
@@ -530,9 +534,9 @@ static void test_c_funcs(void) {
530534

531535
assert((js = js_create(mem, sizeof(mem))) != NULL);
532536
js_set(js, js_glob(js), "gt", js_mkfun(js_gt));
533-
assert(ev(js, "gt()", "ERROR: arg count"));
534-
assert(ev(js, "gt(1,null)", "ERROR: arg 1"));
535-
assert(ev(js, "gt(null, 1)", "ERROR: arg 0"));
537+
assert(ev(js, "gt()", "ERROR: doh"));
538+
assert(ev(js, "gt(1,null)", "ERROR: doh"));
539+
assert(ev(js, "gt(null, 1)", "ERROR: doh"));
536540
assert(ev(js, "gt(1,2)", "false"));
537541
assert(ev(js, "gt(1,1)", "false"));
538542
assert(ev(js, "gt(2,1)", "true"));
@@ -547,6 +551,15 @@ static void test_c_funcs(void) {
547551
if (s_timer_fn) s_timer_fn(1, s_timer_fn_data); // C code calls timer
548552
assert(ev(js, "v", "8"));
549553
// printf("--> [%s]\n", js_str(js, js_glob(js)));
554+
555+
jsval_t args[] = {0, js_mktrue(), js_mkstr(js, "a", 1), js_mknull()};
556+
assert(js_chkargs(args, 4, "dbsj") == true);
557+
assert(js_chkargs(args, 4, "dbsjb") == false);
558+
assert(js_chkargs(args, 4, "bbsj") == false);
559+
assert(js_chkargs(args, 4, "ddsj") == false);
560+
assert(js_chkargs(args, 4, "dbdj") == false);
561+
assert(js_chkargs(args, 4, "dbss") == false);
562+
assert(js_chkargs(args, 4, "d") == false);
550563
}
551564

552565
static void test_ternary(void) {

0 commit comments

Comments
 (0)