Skip to content

Commit

Permalink
Implement pointer arithmetic
Browse files Browse the repository at this point in the history
 - `ptr + 10` is a pointer
 - `10 + ptr` is also a pointer
 - `ptr++`, `ptr--` are also pointers
 - `ptr - ptr2` is a number

Other arithmetic operations with pointers are not allowed

Also, this PR commonizes arithmetic implementation for:
`a + b`, `a += b`, `a++`, `a--`, `--a`, `++a`.

PUBLISHED_FROM=c5eec6f4c59fe36711e7f7f26404dfec2736c9fc
  • Loading branch information
dimonomid authored and cesantabot committed Apr 20, 2017
1 parent ff51690 commit 3980b39
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 54 deletions.
77 changes: 50 additions & 27 deletions mjs.c
Expand Up @@ -6588,24 +6588,56 @@ static void set_no_autoconversion_error(struct mjs *mjs) {
"implicit type conversion is prohibited");
}

static mjs_val_t do_op(struct mjs *mjs, mjs_val_t a, mjs_val_t b, int op) {
mjs_val_t ret = MJS_UNDEFINED;
if ((mjs_is_foreign(a) || mjs_is_number(a)) &&
(mjs_is_foreign(b) || mjs_is_number(b))) {
int is_result_ptr = 0;

if (mjs_is_foreign(a) && mjs_is_foreign(b)) {
/* When two operands are pointers, only subtraction is supported */
if (op != TOK_MINUS) {
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands");
}
} else if (mjs_is_foreign(a) || mjs_is_foreign(b)) {
/*
* When one of the operands is a pointer, only + and - are supported,
* and the result is a pointer.
*/
if (op != TOK_MINUS && op != TOK_PLUS) {
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands");
}
is_result_ptr = 1;
}

double da, db;
da = mjs_is_number(a) ? mjs_get_double(mjs, a)
: (double) (uintptr_t) mjs_get_ptr(mjs, a);
db = mjs_is_number(b) ? mjs_get_double(mjs, b)
: (double) (uintptr_t) mjs_get_ptr(mjs, b);
double result = do_arith_op(da, db, op);

/*
* If at least one of the operands was a pointer, result should also be
* a pointer
*/
ret = is_result_ptr ? mjs_mk_foreign(mjs, (void *) (uintptr_t) result)
: mjs_mk_number(mjs, result);
} else if (mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) {
ret = s_concat(mjs, a, b);
} else {
set_no_autoconversion_error(mjs);
}
return ret;
}

static void op_assign(struct mjs *mjs, int op) {
mjs_val_t val = mjs_pop(mjs);
mjs_val_t obj = mjs_pop(mjs);
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
if (mjs_is_number(v) && mjs_is_number(val)) {
double da = mjs_get_double(mjs, v);
double db = mjs_get_double(mjs, val);
double result = do_arith_op(da, db, op);
mjs_set_v(mjs, obj, key, mjs_mk_number(mjs, result));
} else if (mjs_is_string(v) && mjs_is_string(val) && (op == TOK_PLUS)) {
mjs_val_t result = s_concat(mjs, v, val);
mjs_set_v(mjs, obj, key, result);
} else {
mjs_set_v(mjs, obj, key, MJS_UNDEFINED);
set_no_autoconversion_error(mjs);
}
mjs_set_v(mjs, obj, key, do_op(mjs, v, val, op));
mjs_push(mjs, v);
} else {
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand");
Expand All @@ -6627,18 +6659,9 @@ static void exec_expr(struct mjs *mjs, int op) {
case TOK_LSHIFT:
case TOK_RSHIFT:
case TOK_URSHIFT: {
mjs_val_t a = mjs_pop(mjs);
mjs_val_t b = mjs_pop(mjs);
if (mjs_is_number(a) && mjs_is_number(b)) {
double da = mjs_get_double(mjs, a);
double db = mjs_get_double(mjs, b);
mjs_push(mjs, mjs_mk_number(mjs, do_arith_op(db, da, op)));
} else if (mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) {
mjs_push(mjs, s_concat(mjs, b, a));
} else {
mjs_push(mjs, MJS_UNDEFINED);
set_no_autoconversion_error(mjs);
}
mjs_val_t a = mjs_pop(mjs);
mjs_push(mjs, do_op(mjs, a, b, op));
break;
}
case TOK_UNARY_MINUS: {
Expand Down Expand Up @@ -6733,7 +6756,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
mjs_val_t v1 = mjs_mk_number(mjs, mjs_get_double(mjs, v) + 1);
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);
mjs_set_v(mjs, obj, key, v1);
mjs_push(mjs, v);
} else {
Expand All @@ -6746,7 +6769,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
mjs_val_t v1 = mjs_mk_number(mjs, mjs_get_double(mjs, v) - 1);
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);
mjs_set_v(mjs, obj, key, v1);
mjs_push(mjs, v);
} else {
Expand All @@ -6759,7 +6782,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
v = mjs_mk_number(mjs, mjs_get_double(mjs, v) - 1);
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);
mjs_set_v(mjs, obj, key, v);
mjs_push(mjs, v);
} else {
Expand All @@ -6772,7 +6795,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
v = mjs_mk_number(mjs, mjs_get_double(mjs, v) + 1);
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);
mjs_set_v(mjs, obj, key, v);
mjs_push(mjs, v);
} else {
Expand Down
77 changes: 50 additions & 27 deletions mjs/src/mjs_exec.c
Expand Up @@ -98,24 +98,56 @@ static void set_no_autoconversion_error(struct mjs *mjs) {
"implicit type conversion is prohibited");
}

static mjs_val_t do_op(struct mjs *mjs, mjs_val_t a, mjs_val_t b, int op) {
mjs_val_t ret = MJS_UNDEFINED;
if ((mjs_is_foreign(a) || mjs_is_number(a)) &&
(mjs_is_foreign(b) || mjs_is_number(b))) {
int is_result_ptr = 0;

if (mjs_is_foreign(a) && mjs_is_foreign(b)) {
/* When two operands are pointers, only subtraction is supported */
if (op != TOK_MINUS) {
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands");
}
} else if (mjs_is_foreign(a) || mjs_is_foreign(b)) {
/*
* When one of the operands is a pointer, only + and - are supported,
* and the result is a pointer.
*/
if (op != TOK_MINUS && op != TOK_PLUS) {
mjs_prepend_errorf(mjs, MJS_TYPE_ERROR, "invalid operands");
}
is_result_ptr = 1;
}

double da, db;
da = mjs_is_number(a) ? mjs_get_double(mjs, a)
: (double) (uintptr_t) mjs_get_ptr(mjs, a);
db = mjs_is_number(b) ? mjs_get_double(mjs, b)
: (double) (uintptr_t) mjs_get_ptr(mjs, b);
double result = do_arith_op(da, db, op);

/*
* If at least one of the operands was a pointer, result should also be
* a pointer
*/
ret = is_result_ptr ? mjs_mk_foreign(mjs, (void *) (uintptr_t) result)
: mjs_mk_number(mjs, result);
} else if (mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) {
ret = s_concat(mjs, a, b);
} else {
set_no_autoconversion_error(mjs);
}
return ret;
}

static void op_assign(struct mjs *mjs, int op) {
mjs_val_t val = mjs_pop(mjs);
mjs_val_t obj = mjs_pop(mjs);
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
if (mjs_is_number(v) && mjs_is_number(val)) {
double da = mjs_get_double(mjs, v);
double db = mjs_get_double(mjs, val);
double result = do_arith_op(da, db, op);
mjs_set_v(mjs, obj, key, mjs_mk_number(mjs, result));
} else if (mjs_is_string(v) && mjs_is_string(val) && (op == TOK_PLUS)) {
mjs_val_t result = s_concat(mjs, v, val);
mjs_set_v(mjs, obj, key, result);
} else {
mjs_set_v(mjs, obj, key, MJS_UNDEFINED);
set_no_autoconversion_error(mjs);
}
mjs_set_v(mjs, obj, key, do_op(mjs, v, val, op));
mjs_push(mjs, v);
} else {
mjs_set_errorf(mjs, MJS_TYPE_ERROR, "invalid operand");
Expand All @@ -137,18 +169,9 @@ static void exec_expr(struct mjs *mjs, int op) {
case TOK_LSHIFT:
case TOK_RSHIFT:
case TOK_URSHIFT: {
mjs_val_t a = mjs_pop(mjs);
mjs_val_t b = mjs_pop(mjs);
if (mjs_is_number(a) && mjs_is_number(b)) {
double da = mjs_get_double(mjs, a);
double db = mjs_get_double(mjs, b);
mjs_push(mjs, mjs_mk_number(mjs, do_arith_op(db, da, op)));
} else if (mjs_is_string(a) && mjs_is_string(b) && (op == TOK_PLUS)) {
mjs_push(mjs, s_concat(mjs, b, a));
} else {
mjs_push(mjs, MJS_UNDEFINED);
set_no_autoconversion_error(mjs);
}
mjs_val_t a = mjs_pop(mjs);
mjs_push(mjs, do_op(mjs, a, b, op));
break;
}
case TOK_UNARY_MINUS: {
Expand Down Expand Up @@ -243,7 +266,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
mjs_val_t v1 = mjs_mk_number(mjs, mjs_get_double(mjs, v) + 1);
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);
mjs_set_v(mjs, obj, key, v1);
mjs_push(mjs, v);
} else {
Expand All @@ -256,7 +279,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
mjs_val_t v1 = mjs_mk_number(mjs, mjs_get_double(mjs, v) - 1);
mjs_val_t v1 = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);
mjs_set_v(mjs, obj, key, v1);
mjs_push(mjs, v);
} else {
Expand All @@ -269,7 +292,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
v = mjs_mk_number(mjs, mjs_get_double(mjs, v) - 1);
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_MINUS);
mjs_set_v(mjs, obj, key, v);
mjs_push(mjs, v);
} else {
Expand All @@ -282,7 +305,7 @@ static void exec_expr(struct mjs *mjs, int op) {
mjs_val_t key = mjs_pop(mjs);
if (mjs_is_object(obj) && mjs_is_string(key)) {
mjs_val_t v = mjs_get_v(mjs, obj, key);
v = mjs_mk_number(mjs, mjs_get_double(mjs, v) + 1);
v = do_op(mjs, v, mjs_mk_number(mjs, 1), TOK_PLUS);
mjs_set_v(mjs, obj, key, v);
mjs_push(mjs, v);
} else {
Expand Down
59 changes: 59 additions & 0 deletions mjs/tests/unit_test.c
Expand Up @@ -571,6 +571,10 @@ const void *ffi_get_null(void) {
return NULL;
}

void ffi_set_byte(void *ptr, int val) {
*((char *)ptr) = val;
}

int ffi_test_i2i(int a0, int a1) {
return a0 - a1;
}
Expand Down Expand Up @@ -681,6 +685,7 @@ void ffi_test_cb_viiiiiu(cb_viiiiiu *cb, void *userdata) {
void *stub_dlsym(void *handle, const char *name) {
(void) handle;
if (strcmp(name, "ffi_get_null") == 0) return ffi_get_null;
if (strcmp(name, "ffi_set_byte") == 0) return ffi_set_byte;
if (strcmp(name, "ffi_test_i2i") == 0) return ffi_test_i2i;
if (strcmp(name, "ffi_test_iib") == 0) return ffi_test_iib;
if (strcmp(name, "ffi_test_bi") == 0) return ffi_test_bi;
Expand Down Expand Up @@ -2514,6 +2519,8 @@ const char *test_foreign_ptr() {

mjs_set_ffi_resolver(mjs, stub_dlsym);

uint8_t *ptr = NULL;

ASSERT_EXEC_OK(mjs_exec(mjs,
STRINGIFY(
let get_null = ffi('void *ffi_get_null()');
Expand All @@ -2532,6 +2539,58 @@ const char *test_foreign_ptr() {
), &res));
ASSERT_EQ(mjs_get_bool(mjs, res), 0);

ASSERT_EXEC_OK(mjs_exec(mjs,
STRINGIFY(
let set_byte = ffi('void *ffi_set_byte(void *, int)');
let calloc = ffi('void *calloc(int, int)');
let free = ffi('void free(void *)');
let ptr = calloc(100, 1);
ptr;
), &res));
ptr = (uint8_t *)mjs_get_ptr(mjs, res);

ASSERT_EXEC_OK(mjs_exec(mjs,
STRINGIFY(
set_byte(ptr, 0xf0);

let ptr2 = ptr + 10;
set_byte(ptr2, 0xf1);

ptr2 += 10;
set_byte(ptr2, 0xf2);

set_byte(ptr2-1, 0xf3);

ptr2 -= 2;
set_byte(ptr2, 0xf4);

ptr2 = 5 + ptr;
set_byte(ptr + 1, ptr2 - ptr);

ptr2++;
set_byte(ptr + 2, ptr2 - ptr);

ptr2--;
set_byte(ptr + 3, ptr2 - ptr);
), &res));

ASSERT_EQ(ptr[0], 0xf0);
ASSERT_EQ(ptr[10], 0xf1);
ASSERT_EQ(ptr[20], 0xf2);
ASSERT_EQ(ptr[19], 0xf3);
ASSERT_EQ(ptr[18], 0xf4);
ASSERT_EQ(ptr[1], 5);
ASSERT_EQ(ptr[2], 6);
ASSERT_EQ(ptr[3], 5);

/* ptr + ptr2 is not allowed */
ASSERT_EQ(mjs_exec( mjs, "ptr + ptr2", &res), MJS_TYPE_ERROR);

/* ptr * ptr2 is also not allowed */
ASSERT_EQ(mjs_exec( mjs, "ptr * ptr2", &res), MJS_TYPE_ERROR);

free(ptr);

return NULL;
}

Expand Down

0 comments on commit 3980b39

Please sign in to comment.