summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-01-04 16:16:28 +0100
committerGitHub <noreply@github.com>2022-01-04 16:16:28 +0100
commit1377e23afff90128b18ac60c10071295fc0afbab (patch)
tree04397dab9be96a5978e08366299671a8aa507267
parent8907ce41a36f8d42097d884550fb3cfbba62e6c5 (diff)
parentb605dbfcf04f310e08634b52507da7a4155bfce1 (diff)
Merge pull request #30 from jow-/rework-number-handling
treewide: rework numeric value handling
-rw-r--r--CMakeLists.txt7
-rw-r--r--compiler.c55
-rw-r--r--include/ucode/types.h32
-rw-r--r--include/ucode/vallist.h2
-rw-r--r--lexer.c40
-rw-r--r--lib.c158
-rw-r--r--lib/math.c40
-rw-r--r--lib/nl80211.c74
-rw-r--r--lib/rtnl.c74
-rw-r--r--tests/custom/00_syntax/10_numeric_literals6
-rw-r--r--tests/custom/01_arithmetic/02_modulo23
-rw-r--r--tests/custom/01_arithmetic/03_bitwise10
-rw-r--r--tests/custom/01_arithmetic/05_overflow54
-rw-r--r--types.c292
-rw-r--r--vallist.c83
-rw-r--r--vm.c338
16 files changed, 760 insertions, 528 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 82d0e33..e668751 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ include(CheckFunctionExists)
include(CheckSymbolExists)
PROJECT(ucode C)
-ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -ffunction-sections -D_GNU_SOURCE)
+ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -ffunction-sections -fwrapv -D_GNU_SOURCE)
IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
@@ -66,6 +66,11 @@ IF (NOT DLOPEN_FUNCTION_EXISTS)
TARGET_LINK_LIBRARIES(libucode dl)
ENDIF()
+CHECK_FUNCTION_EXISTS(fmod FMOD_FUNCTION_EXISTS)
+IF (NOT FMOD_FUNCTION_EXISTS)
+ TARGET_LINK_LIBRARIES(libucode m)
+ENDIF()
+
SET(CMAKE_REQUIRED_LIBRARIES json-c)
CHECK_SYMBOL_EXISTS(json_object_array_shrink "json.h" HAVE_ARRAY_SHRINK)
IF(HAVE_ARRAY_SHRINK)
diff --git a/compiler.c b/compiler.c
index e7bbd7f..b6793d7 100644
--- a/compiler.c
+++ b/compiler.c
@@ -15,6 +15,7 @@
*/
#include <assert.h>
+#include <errno.h>
#include "ucode/compiler.h"
#include "ucode/chunk.h"
@@ -410,15 +411,6 @@ uc_compiler_emit_u8(uc_compiler_t *compiler, size_t srcpos, uint8_t n)
}
static size_t
-uc_compiler_emit_s8(uc_compiler_t *compiler, size_t srcpos, int8_t n)
-{
- return uc_chunk_add(
- uc_compiler_current_chunk(compiler),
- n + 0x7f,
- uc_compiler_set_srcpos(compiler, srcpos));
-}
-
-static size_t
uc_compiler_emit_u16(uc_compiler_t *compiler, size_t srcpos, uint16_t n)
{
uc_chunk_t *chunk = uc_compiler_current_chunk(compiler);
@@ -431,19 +423,6 @@ uc_compiler_emit_u16(uc_compiler_t *compiler, size_t srcpos, uint16_t n)
}
static size_t
-uc_compiler_emit_s16(uc_compiler_t *compiler, size_t srcpos, int16_t n)
-{
- uc_chunk_t *chunk = uc_compiler_current_chunk(compiler);
- size_t lineoff = uc_compiler_set_srcpos(compiler, srcpos);
- uint16_t v = n + 0x7fff;
-
- uc_chunk_add(chunk, v / 0x100, lineoff);
- uc_chunk_add(chunk, v % 0x100, 0);
-
- return chunk->count - 2;
-}
-
-static size_t
uc_compiler_emit_u32(uc_compiler_t *compiler, size_t srcpos, uint32_t n)
{
uc_chunk_t *chunk = uc_compiler_current_chunk(compiler);
@@ -866,6 +845,7 @@ uc_compiler_emit_inc_dec(uc_compiler_t *compiler, uc_tokentype_t toktype, bool i
uc_value_t *varname = NULL;
uc_vm_insn_t type;
uint32_t cidx = 0;
+ int insn;
/* determine kind of emitted load instruction and operand value (if any) */
type = chunk->entries ? chunk->entries[compiler->last_insn] : 0;
@@ -901,30 +881,32 @@ uc_compiler_emit_inc_dec(uc_compiler_t *compiler, uc_tokentype_t toktype, bool i
return;
}
+ insn = (toktype == TK_INC) ? I_PLUS : I_MINUS;
+
/* add / substract 1 */
uc_compiler_emit_insn(compiler, 0, I_LOAD8);
- uc_compiler_emit_s8(compiler, 0, (toktype == TK_INC) ? 1 : -1);
+ uc_compiler_emit_u8(compiler, 0, 1);
/* depending on variable type, emit corresponding increment instruction */
switch (type) {
case I_LVAR:
uc_compiler_emit_insn(compiler, 0, I_UVAR);
- uc_compiler_emit_u32(compiler, 0, (I_PLUS << 24) | cidx);
+ uc_compiler_emit_u32(compiler, 0, (insn << 24) | cidx);
break;
case I_LLOC:
uc_compiler_emit_insn(compiler, 0, I_ULOC);
- uc_compiler_emit_u32(compiler, 0, (I_PLUS << 24) | cidx);
+ uc_compiler_emit_u32(compiler, 0, (insn << 24) | cidx);
break;
case I_LUPV:
uc_compiler_emit_insn(compiler, 0, I_UUPV);
- uc_compiler_emit_u32(compiler, 0, (I_PLUS << 24) | cidx);
+ uc_compiler_emit_u32(compiler, 0, (insn << 24) | cidx);
break;
case I_LVAL:
uc_compiler_emit_insn(compiler, 0, I_UVAL);
- uc_compiler_emit_u8(compiler, 0, I_PLUS);
+ uc_compiler_emit_u8(compiler, 0, insn);
break;
default:
@@ -934,7 +916,7 @@ uc_compiler_emit_inc_dec(uc_compiler_t *compiler, uc_tokentype_t toktype, bool i
/* for post increment or decrement, add/substract 1 to yield final value */
if (is_postfix) {
uc_compiler_emit_insn(compiler, 0, I_LOAD8);
- uc_compiler_emit_s8(compiler, 0, 1);
+ uc_compiler_emit_u8(compiler, 0, 1);
uc_compiler_emit_insn(compiler, 0, (toktype == TK_INC) ? I_SUB : I_ADD);
}
@@ -1466,7 +1448,7 @@ static void
uc_compiler_compile_constant(uc_compiler_t *compiler)
{
uc_function_t *fn;
- int64_t n;
+ uint64_t u;
switch (compiler->parser->prev.type) {
case TK_THIS:
@@ -1502,19 +1484,20 @@ uc_compiler_compile_constant(uc_compiler_t *compiler)
break;
case TK_NUMBER:
- n = ucv_int64_get(compiler->parser->prev.uv);
+ u = ucv_uint64_get(compiler->parser->prev.uv);
+ assert(errno == 0);
- if (n >= -0x7f && n <= 0x7f) {
+ if (u <= 0xff) {
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_LOAD8);
- uc_compiler_emit_s8(compiler, compiler->parser->prev.pos, n);
+ uc_compiler_emit_u8(compiler, compiler->parser->prev.pos, u);
}
- else if (n >= -0x7fff && n <= 0x7fff) {
+ else if (u <= 0xffff) {
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_LOAD16);
- uc_compiler_emit_s16(compiler, compiler->parser->prev.pos, n);
+ uc_compiler_emit_u16(compiler, compiler->parser->prev.pos, u);
}
- else if (n >= -0x7fffffff && n <= 0x7fffffff) {
+ else if (u <= 0xffffffff) {
uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_LOAD32);
- uc_compiler_emit_s32(compiler, compiler->parser->prev.pos, n);
+ uc_compiler_emit_u32(compiler, compiler->parser->prev.pos, u);
}
else {
uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv);
diff --git a/include/ucode/types.h b/include/ucode/types.h
index 49d910b..cbd03dd 100644
--- a/include/ucode/types.h
+++ b/include/ucode/types.h
@@ -372,24 +372,46 @@ void ucv_to_stringbuf_formatted(uc_vm_t *, uc_stringbuf_t *, uc_value_t *, size_
uc_type_t ucv_cast_number(uc_value_t *, int64_t *, double *);
+uc_value_t *ucv_to_number(uc_value_t *);
+
static inline double
ucv_to_double(uc_value_t *v)
{
- int64_t n;
+ uc_value_t *nv;
double d;
- return (ucv_cast_number(v, &n, &d) == UC_DOUBLE) ? d : (double)n;
+ nv = ucv_to_number(v);
+ d = ucv_double_get(nv);
+ ucv_put(nv);
+
+ return d;
}
static inline int64_t
ucv_to_integer(uc_value_t *v)
{
+ uc_value_t *nv;
int64_t n;
- double d;
- return (ucv_cast_number(v, &n, &d) == UC_DOUBLE) ? (int64_t)d : n;
+ nv = ucv_to_number(v);
+ n = ucv_int64_get(nv);
+ ucv_put(nv);
+
+ return n;
}
+static inline uint64_t
+ucv_to_unsigned(uc_value_t *v)
+{
+ uc_value_t *nv;
+ uint64_t u;
+
+ nv = ucv_to_number(v);
+ u = ucv_uint64_get(nv);
+ ucv_put(nv);
+
+ return u;
+}
static inline bool
ucv_is_callable(uc_value_t *uv)
@@ -437,7 +459,7 @@ ucv_is_scalar(uc_value_t *uv)
bool ucv_is_equal(uc_value_t *, uc_value_t *);
bool ucv_is_truish(uc_value_t *);
-bool ucv_compare(int, uc_value_t *, uc_value_t *);
+bool ucv_compare(int, uc_value_t *, uc_value_t *, int *);
uc_value_t *ucv_key_get(uc_vm_t *, uc_value_t *, uc_value_t *);
uc_value_t *ucv_key_set(uc_vm_t *, uc_value_t *, uc_value_t *, uc_value_t *);
diff --git a/include/ucode/vallist.h b/include/ucode/vallist.h
index a3d94e8..47ddc7d 100644
--- a/include/ucode/vallist.h
+++ b/include/ucode/vallist.h
@@ -41,6 +41,8 @@ typedef enum {
TAG_PTR = 6
} uc_value_type_t;
+uc_value_t *uc_number_parse(const char *buf, char **end);
+
void uc_vallist_init(uc_value_list_t *list);
void uc_vallist_free(uc_value_list_t *list);
diff --git a/lexer.c b/lexer.c
index c5314ae..7637306 100644
--- a/lexer.c
+++ b/lexer.c
@@ -121,7 +121,7 @@ static const struct token tokens[] = {
{ TK_RPAREN, { .pat = ")" }, 1, NULL },
{ TK_QMARK, { .pat = "?" }, 1, NULL },
{ TK_SCOL, { .pat = ";" }, 1, NULL },
- //{ TK_SUB, { .pat = "-" }, 1, NULL },
+ { TK_SUB, { .pat = "-" }, 1, NULL },
{ TK_DOT, { .pat = "." }, 1, NULL },
{ TK_STRING, { .pat = "'" }, 1, parse_string },
{ TK_STRING, { .pat = "\"" }, 1, parse_string },
@@ -129,7 +129,6 @@ static const struct token tokens[] = {
{ TK_LABEL, { .pat = "_" }, 1, parse_label },
{ TK_LABEL, { .pat = "az" }, 0, parse_label },
{ TK_LABEL, { .pat = "AZ" }, 0, parse_label },
- { TK_NUMBER, { .pat = "-" }, 1, parse_number },
{ TK_NUMBER, { .pat = "09" }, 0, parse_number },
};
@@ -779,39 +778,26 @@ is_numeric_char(uc_lexer_t *lex, char c)
static uc_token_t *
parse_number(uc_lexer_t *lex)
{
- const struct token *tok = lex->tok;
uc_token_t *rv = NULL;
- long long int n;
- char *ptr, *e;
- double d;
+ uc_value_t *nv = NULL;
+ const char *ptr;
+ char *e;
if (!buf_remaining(lex) || !is_numeric_char(lex, lex->bufstart[0])) {
- if (lex->lookbehindlen == 0 && !is_numeric_char(lex, lex->bufstart[0]))
- return emit_op(lex, lex->source->off, TK_SUB, NULL);
-
lookbehind_append(lex, "\0", 1);
- n = strtoll(lex->lookbehind, &e, 0);
-
- if (*e == '.' || *e == 'e' || *e == 'E') {
- d = strtod(lex->lookbehind, &e);
+ nv = uc_number_parse(lex->lookbehind, &e);
- if (tok->u.pat[0] == '-')
- d = -d;
+ switch (ucv_type(nv)) {
+ case UC_DOUBLE:
+ rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_DOUBLE, nv);
+ break;
- if (e > lex->lookbehind && *e == 0)
- rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_DOUBLE, ucv_double_new(d));
- else
- rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal"));
- }
- else if (*e == 0) {
- if (tok->u.pat[0] == '-')
- n = (errno == ERANGE) ? INT64_MIN : -n;
+ case UC_INTEGER:
+ rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, nv);
+ break;
- rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, ucv_int64_new(n));
- //OP(rv)->is_overflow = (errno == ERANGE);
- }
- else {
+ default:
rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal"));
}
diff --git a/lib.c b/lib.c
index aa4d43f..9a8f6b8 100644
--- a/lib.c
+++ b/lib.c
@@ -216,52 +216,6 @@ static char *uc_cast_string(uc_vm_t *vm, uc_value_t **v, bool *freeable) {
return ucv_to_string(vm, *v);
}
-static double
-uc_cast_double(uc_value_t *v)
-{
- uc_type_t t;
- int64_t n;
- double d;
-
- t = ucv_cast_number(v, &n, &d);
- errno = 0;
-
- if (t == UC_DOUBLE) {
- if (isnan(d))
- errno = EINVAL;
- else if (!isfinite(d))
- errno = EOVERFLOW;
-
- return d;
- }
-
- return (double)n;
-}
-
-static int64_t
-uc_cast_int64(uc_value_t *v)
-{
- uc_type_t t;
- int64_t n;
- double d;
-
- t = ucv_cast_number(v, &n, &d);
- errno = 0;
-
- if (t == UC_DOUBLE) {
- if (isnan(d))
- errno = EINVAL;
- else if (!isfinite(d))
- errno = EOVERFLOW;
- else if ((double)(int64_t)d != d)
- errno = ERANGE;
-
- return (int64_t)d;
- }
-
- return n;
-}
-
static void
uc_vm_ctx_push(uc_vm_t *vm)
{
@@ -339,7 +293,7 @@ uc_index(uc_vm_t *vm, size_t nargs, bool right)
switch (ucv_type(stack)) {
case UC_ARRAY:
for (arridx = 0, len = ucv_array_length(stack); arridx < len; arridx++) {
- if (ucv_compare(I_EQ, ucv_array_get(stack, arridx), needle)) {
+ if (ucv_compare(I_EQ, ucv_array_get(stack, arridx), needle, NULL)) {
ret = (ssize_t)arridx;
if (!right)
@@ -448,7 +402,7 @@ uc_chr(uc_vm_t *vm, size_t nargs)
str = xalloc(nargs);
for (idx = 0; idx < nargs; idx++) {
- n = uc_cast_int64(uc_fn_arg(idx));
+ n = ucv_to_integer(uc_fn_arg(idx));
if (n < 0)
n = 0;
@@ -505,7 +459,7 @@ uc_exists(uc_vm_t *vm, size_t nargs)
static uc_value_t *
uc_exit(uc_vm_t *vm, size_t nargs)
{
- int64_t n = uc_cast_int64(uc_fn_arg(0));
+ int64_t n = ucv_to_integer(uc_fn_arg(0));
vm->arg.s32 = (int32_t)n;
uc_vm_raise_exception(vm, EXCEPTION_EXIT, "Terminated");
@@ -583,9 +537,9 @@ uc_hex(uc_vm_t *vm, size_t nargs)
static uc_value_t *
uc_int(uc_vm_t *vm, size_t nargs)
{
- int64_t n = uc_cast_int64(uc_fn_arg(0));
+ int64_t n = ucv_to_integer(uc_fn_arg(0));
- if (errno == EINVAL || errno == EOVERFLOW)
+ if (errno == EINVAL || errno == ERANGE)
return ucv_double_new(NAN);
return ucv_int64_new(n);
@@ -796,59 +750,37 @@ static struct {
static int
default_cmp(uc_value_t *v1, uc_value_t *v2)
{
- uc_type_t t1, t2;
- int64_t n1, n2;
- double d1, d2;
char *s1, *s2;
bool f1, f2;
+ int res;
- if (ucv_type(v1) == UC_INTEGER || ucv_type(v1) == UC_DOUBLE ||
- ucv_type(v2) == UC_INTEGER || ucv_type(v2) == UC_DOUBLE) {
- t1 = ucv_cast_number(v1, &n1, &d1);
- t2 = ucv_cast_number(v2, &n2, &d2);
-
- if (t1 == UC_DOUBLE || t2 == UC_DOUBLE) {
- d1 = (t1 == UC_DOUBLE) ? d1 : (double)n1;
- d2 = (t2 == UC_DOUBLE) ? d2 : (double)n2;
-
- if (d1 < d2)
- return -1;
+ /* when both operands are numeric then compare numerically */
+ if ((ucv_type(v1) == UC_INTEGER || ucv_type(v1) == UC_DOUBLE) &&
+ (ucv_type(v2) == UC_INTEGER || ucv_type(v2) == UC_DOUBLE)) {
+ ucv_compare(0, v1, v2, &res);
- if (d1 > d2)
- return 1;
-
- return 0;
- }
-
- if (n1 < n2)
- return -1;
-
- if (n1 > n2)
- return 1;
-
- return 0;
+ return res;
}
+ /* otherwise convert both operands to strings and compare lexically */
s1 = uc_cast_string(sort_ctx.vm, &v1, &f1);
s2 = uc_cast_string(sort_ctx.vm, &v2, &f2);
- n1 = strcmp(s1, s2);
+ res = strcmp(s1, s2);
if (f1) free(s1);
if (f2) free(s2);
- return n1;
+ return res;
}
static int
sort_fn(const void *k1, const void *k2)
{
+ uc_value_t *rv, *null = ucv_int64_new(0);
uc_value_t * const *v1 = k1;
uc_value_t * const *v2 = k2;
- uc_value_t *rv;
- uc_type_t t;
- int64_t n;
- double d;
+ int res;
if (!sort_ctx.fn)
return default_cmp(*v1, *v2);
@@ -868,26 +800,13 @@ sort_fn(const void *k1, const void *k2)
}
rv = uc_vm_stack_pop(sort_ctx.vm);
- t = ucv_cast_number(rv, &n, &d);
-
- if (t == UC_DOUBLE) {
- if (d < 0)
- n = -1;
-
- if (d > 0)
- n = 1;
- }
- else {
- if (n < 0)
- n = -1;
- if (n > 0)
- n = 1;
- }
+ ucv_compare(0, rv, null, &res);
+ ucv_put(null);
ucv_put(rv);
- return n;
+ return res;
}
static uc_value_t *
@@ -911,8 +830,8 @@ static uc_value_t *
uc_splice(uc_vm_t *vm, size_t nargs)
{
uc_value_t *arr = uc_fn_arg(0);
- int64_t ofs = uc_cast_int64(uc_fn_arg(1));
- int64_t remlen = uc_cast_int64(uc_fn_arg(2));
+ int64_t ofs = ucv_to_integer(uc_fn_arg(1));
+ int64_t remlen = ucv_to_integer(uc_fn_arg(2));
size_t arrlen, addlen, idx;
if (ucv_type(arr) != UC_ARRAY)
@@ -1043,8 +962,8 @@ static uc_value_t *
uc_substr(uc_vm_t *vm, size_t nargs)
{
uc_value_t *str = uc_fn_arg(0);
- int64_t ofs = uc_cast_int64(uc_fn_arg(1));
- int64_t sublen = uc_cast_int64(uc_fn_arg(2));
+ int64_t ofs = ucv_to_integer(uc_fn_arg(1));
+ int64_t sublen = ucv_to_integer(uc_fn_arg(2));
const char *p;
size_t len;
@@ -1142,9 +1061,9 @@ uc_uchr(uc_vm_t *vm, size_t nargs)
int rem;
for (idx = 0, ulen = 0; idx < nargs; idx++) {
- n = uc_cast_int64(uc_fn_arg(idx));
+ n = ucv_to_integer(uc_fn_arg(idx));
- if (errno == EINVAL || errno == EOVERFLOW || n < 0 || n > 0x10FFFF)
+ if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
ulen += 3;
else if (n <= 0x7F)
ulen++;
@@ -1159,9 +1078,9 @@ uc_uchr(uc_vm_t *vm, size_t nargs)
str = xalloc(ulen);
for (idx = 0, p = str, rem = ulen; idx < nargs; idx++) {
- n = uc_cast_int64(uc_fn_arg(idx));
+ n = ucv_to_integer(uc_fn_arg(idx));
- if (errno == EINVAL || errno == EOVERFLOW || n < 0 || n > 0x10FFFF)
+ if (errno == EINVAL || errno == ERANGE || n < 0 || n > 0x10FFFF)
n = 0xFFFD;
if (!utf8enc(&p, &rem, n))
@@ -1344,10 +1263,17 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
case 'X':
t = UC_INTEGER;
- if (argidx < nargs)
- arg.n = uc_cast_int64(uc_fn_arg(argidx++));
- else
+ if (argidx < nargs) {
+ arg.n = ucv_to_integer(uc_fn_arg(argidx));
+
+ if (errno == ERANGE)
+ arg.n = (int64_t)ucv_to_unsigned(uc_fn_arg(argidx));
+
+ argidx++;
+ }
+ else {
arg.n = 0;
+ }
break;
@@ -1360,7 +1286,7 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
t = UC_DOUBLE;
if (argidx < nargs)
- arg.d = uc_cast_double(uc_fn_arg(argidx++));
+ arg.d = ucv_to_double(uc_fn_arg(argidx++));
else
arg.d = 0;
@@ -1370,7 +1296,7 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
t = UC_INTEGER;
if (argidx < nargs)
- arg.n = uc_cast_int64(uc_fn_arg(argidx++)) & 0xff;
+ arg.n = ucv_to_integer(uc_fn_arg(argidx++)) & 0xff;
else
arg.n = 0;
@@ -2436,7 +2362,7 @@ uc_sleep(uc_vm_t *vm, size_t nargs)
struct timeval tv;
int64_t ms;
- ms = uc_cast_int64(duration);
+ ms = ucv_to_integer(duration);
if (errno != 0 || ms <= 0)
return ucv_boolean_new(false);
@@ -2562,7 +2488,7 @@ uc_sourcepath(uc_vm_t *vm, size_t nargs)
int64_t depth;
size_t i;
- depth = uc_cast_int64(calldepth);
+ depth = ucv_to_integer(calldepth);
if (errno)
depth = 0;
@@ -2604,7 +2530,7 @@ uc_min_max(uc_vm_t *vm, size_t nargs, int cmp)
for (i = 0; i < nargs; i++) {
val = uc_fn_arg(i);
- if (!set || ucv_compare(cmp, val, rv)) {
+ if (!set || ucv_compare(cmp, val, rv, NULL)) {
set = true;
rv = val;
}
diff --git a/lib/math.c b/lib/math.c
index c4a08e3..f16d309 100644
--- a/lib/math.c
+++ b/lib/math.c
@@ -15,6 +15,7 @@
*/
#include <math.h>
+#include <errno.h>
#include <sys/time.h>
#include "ucode/module.h"
@@ -24,20 +25,43 @@ static bool srand_called = false;
static uc_value_t *
uc_abs(uc_vm_t *vm, size_t nargs)
{
- uc_value_t *v = uc_fn_arg(0);
- uc_type_t t;
+ uc_value_t *v = uc_fn_arg(0), *nv, *res;
int64_t n;
double d;
- if (ucv_type(v) == UC_NULL)
- return ucv_double_new(NAN);
+ nv = v ? ucv_to_number(v) : NULL;
+
+ switch (ucv_type(nv)) {
+ case UC_INTEGER:
+ n = ucv_int64_get(nv);
+
+ if (n >= 0 || errno == ERANGE)
+ res = ucv_get(nv);
+ else if (n == INT64_MIN)
+ res = ucv_uint64_new((uint64_t)INT64_MAX + 1);
+ else
+ res = ucv_uint64_new(-n);
+
+ break;
- t = ucv_cast_number(v, &n, &d);
+ case UC_DOUBLE:
+ d = ucv_double_get(nv);
+
+ if (isnan(d) || d >= 0)
+ res = ucv_get(nv);
+ else
+ res = ucv_double_new(-d);
+
+ break;
+
+ default:
+ res = ucv_double_new(NAN);
+ break;
+ }
- if (t == UC_DOUBLE)
- return (isnan(d) || d < 0) ? ucv_double_new(-d) : ucv_get(v);
+ ucv_put(nv);
- return (n < 0) ? ucv_int64_new(-n) : ucv_get(v);
+ return res;
}
static uc_value_t *
diff --git a/lib/nl80211.c b/lib/nl80211.c
index b48ff2a..8504203 100644
--- a/lib/nl80211.c
+++ b/lib/nl80211.c
@@ -69,29 +69,14 @@ set_error(int errcode, const char *fmt, ...) {
static bool
uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
{
- uc_type_t t;
- int64_t i;
- double d;
-
- t = ucv_cast_number(val, &i, &d);
-
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < 0 || d > UINT32_MAX)
- return false;
-
- i = (int64_t)d;
+ uint64_t u;
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
+ u = ucv_to_unsigned(val);
- if (i < 0 || i > UINT32_MAX)
+ if (errno != 0 || u > UINT32_MAX)
return false;
- *n = (uint32_t)i;
+ *n = (uint32_t)u;
return true;
}
@@ -99,26 +84,11 @@ uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
static bool
uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
{
- uc_type_t t;
int64_t i;
- double d;
-
- t = ucv_cast_number(val, &i, &d);
-
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < INT32_MIN || d > INT32_MAX)
- return false;
-
- i = (int64_t)d;
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
+ i = ucv_to_integer(val);
- if (i < INT32_MIN || i > INT32_MAX)
+ if (errno != 0 || i < INT32_MIN || i > INT32_MAX)
return false;
*n = (uint32_t)i;
@@ -129,37 +99,9 @@ uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
static bool
uc_nl_parse_u64(uc_value_t *val, uint64_t *n)
{
- uc_type_t t;
- int64_t i;
- double d;
-
- if (ucv_type(val) == UC_INTEGER) {
- *n = ucv_uint64_get(val);
-
- return true;
- }
-
- t = ucv_cast_number(val, &i, &d);
-
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < 0)
- return false;
-
- i = (int64_t)d;
+ *n = ucv_to_unsigned(val);
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
-
- if (i < 0)
- return false;
-
- *n = (uint64_t)i;
-
- return true;
+ return (errno == 0);
}
static bool
diff --git a/lib/rtnl.c b/lib/rtnl.c
index 2393264..4d5cc02 100644
--- a/lib/rtnl.c
+++ b/lib/rtnl.c
@@ -97,29 +97,14 @@ typedef struct {
static bool
uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
{
- uc_type_t t;
- int64_t i;
- double d;
-
- t = ucv_cast_number(val, &i, &d);
-
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < 0 || d > UINT32_MAX)
- return false;
-
- i = (int64_t)d;
+ uint64_t u;
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
+ u = ucv_to_unsigned(val);
- if (i < 0 || i > UINT32_MAX)
+ if (errno != 0 || u > UINT32_MAX)
return false;
- *n = (uint32_t)i;
+ *n = (uint32_t)u;
return true;
}
@@ -127,26 +112,11 @@ uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
static bool
uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
{
- uc_type_t t;
int64_t i;
- double d;
-
- t = ucv_cast_number(val, &i, &d);
-
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < INT32_MIN || d > INT32_MAX)
- return false;
-
- i = (int64_t)d;
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
+ i = ucv_to_integer(val);
- if (i < INT32_MIN || i > INT32_MAX)
+ if (errno != 0 || i < INT32_MIN || i > INT32_MAX)
return false;
*n = (uint32_t)i;
@@ -157,37 +127,9 @@ uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
static bool
uc_nl_parse_u64(uc_value_t *val, uint64_t *n)
{
- uc_type_t t;
- int64_t i;
- double d;
-
- if (ucv_type(val) == UC_INTEGER) {
- *n = ucv_uint64_get(val);
-
- return true;
- }
-
- t = ucv_cast_number(val, &i, &d);
+ *n = ucv_to_unsigned(val);
- if (t == UC_DOUBLE) {
- if (isnan(d) || d < 0)
- return false;
-
- i = (int64_t)d;
-
- if (d - i > 0)
- return false;
- }
- else if (errno != 0) {
- return false;
- }
-
- if (i < 0)
- return false;
-
- *n = (uint64_t)i;
-
- return true;
+ return (errno == 0);
}
static const char *
diff --git a/tests/custom/00_syntax/10_numeric_literals b/tests/custom/00_syntax/10_numeric_literals
index 3e367d0..7aa49b6 100644
--- a/tests/custom/00_syntax/10_numeric_literals
+++ b/tests/custom/00_syntax/10_numeric_literals
@@ -12,7 +12,7 @@ Special values: Infinity, Infinity, NaN, NaN
Minimum values: -9223372036854775808, -1.79769e+308
Maximum values: 9223372036854775807, 1.79769e+308
Minimum truncation: -9223372036854775808, -Infinity
-Maximum truncation: 9223372036854775807, Infinity
+Maximum truncation: 18446744073709551615, Infinity
-- End --
-- Testcase --
@@ -21,6 +21,6 @@ Float literals: {{ 10. }}, {{ 10.3 }}, {{ 123.456e-67 }}, {{ 0x10.1 }}
Special values: {{ Infinity }}, {{ 1 / 0 }}, {{ NaN }}, {{ "x" / 1 }}
Minimum values: {{ -9223372036854775808 }}, {{ -1.7976931348623158e+308 }}
Maximum values: {{ 9223372036854775807 }}, {{ 1.7976931348623158e+308 }}
-Minimum truncation: {{ -10000000000000000000 }}, {{ -1.0e309 }}
-Maximum truncation: {{ 10000000000000000000 }}, {{ 1.0e309 }}
+Minimum truncation: {{ -100000000000000000000 }}, {{ -1.0e309 }}
+Maximum truncation: {{ 100000000000000000000 }}, {{ 1.0e309 }}
-- End --
diff --git a/tests/custom/01_arithmetic/02_modulo b/tests/custom/01_arithmetic/02_modulo
index 244d624..c011e04 100644
--- a/tests/custom/01_arithmetic/02_modulo
+++ b/tests/custom/01_arithmetic/02_modulo
@@ -1,32 +1,31 @@
-The utpl language supports modulo divisions, however they're only defined
-for integer values.
+The ucode language supports modulo divisions.
-- Expect stdout --
If both operands are integers or convertible to integers,
-the modulo division yields the remainder:
+the modulo division yields the remaining integer value:
10 % 4 = 2
"10" % 4 = 2
10 % "4" = 2
"10" % "4" = 2
If either operand is a double value, the modulo operation
-yields NaN:
-10.0 % 4 = NaN
-10 % 4.0 = NaN
-"10.0" % 4 = NaN
+yields the remaining value as calculated by fmod(3):
+10.2 % 4 = 2.2
+10 % 4.3 = 1.4
+"10.4" % 4 = 2.4
-- End --
-- Testcase --
If both operands are integers or convertible to integers,
-the modulo division yields the remainder:
+the modulo division yields the remaining integer value:
10 % 4 = {{ 10 % 4 }}
"10" % 4 = {{ "10" % 4 }}
10 % "4" = {{ 10 % 4 }}
"10" % "4" = {{ "10" % "4" }}
If either operand is a double value, the modulo operation
-yields NaN:
-10.0 % 4 = {{ 10.0 % 4 }}
-10 % 4.0 = {{ 10 % 4.0 }}
-"10.0" % 4 = {{ "10.0" % 4 }}
+yields the remaining value as calculated by fmod(3):
+10.2 % 4 = {{ 10.2 % 4 }}
+10 % 4.3 = {{ 10 % 4.3 }}
+"10.4" % 4 = {{ "10.4" % 4 }}
-- End --
diff --git a/tests/custom/01_arithmetic/03_bitwise b/tests/custom/01_arithmetic/03_bitwise
index faf4ffd..c481b3e 100644
--- a/tests/custom/01_arithmetic/03_bitwise
+++ b/tests/custom/01_arithmetic/03_bitwise
@@ -1,5 +1,7 @@
-Utpl implements C-style bitwise operations. One detail is that these operations
-coerce their operands to signed 64bit integer values internally.
+Ucode implements C-style bitwise operations. One detail is that these operations
+coerce their operands to 64bit integer values internally. If both operands are
+positive, unsigned 64bit semantics are used. If one of the operands is negative,
+both are converted to signed 64bit numbers.
-- Expect stdout --
Left shift:
@@ -23,8 +25,8 @@ Bitwise or:
120.3 | 54.3 = 126
Complement:
-~0 = -1
-~10.4 = -11
+~0 = 18446744073709551615
+~10.4 = 18446744073709551605
-- End --
-- Testcase --
diff --git a/tests/custom/01_arithmetic/05_overflow b/tests/custom/01_arithmetic/05_overflow
new file mode 100644
index 0000000..8c2f214
--- /dev/null
+++ b/tests/custom/01_arithmetic/05_overflow
@@ -0,0 +1,54 @@
+For integers, the ucode VM tries to perform unsigned 64bit arithmetic internally
+if both operands are positive or if the result is guaranteed to be positive.
+
+In all other cases, calculations are performed using signed 64bit arithmetic
+with wrap arounds using twos-complement representation.
+
+Due to this, the minimum and maximum representable values depend on the values
+of the involved operands.
+
+-- Testcase --
+Unsigned additions roll over back to zero:
+{{ 18446744073709551615 + 1 }}
+
+Unsigned multiplications roll over back to zero:
+{{ 9223372036854775808 * 2 }}
+
+Signed additions roll over at INT64_MIN/INT64_MAX:
+{{ -9223372036854775808 + -1 }}
+
+Signed multiplications roll over back to INT64_MIN:
+{{ 18446744073709551615 * -1 }}
+
+Multiplicating two negative operands yields an unsigned result.
+{{ -9223372036854775807 * -2 }}
+
+Signed calculations yielding positive results are promoted to unsigned.
+{{ -9223372036854775808 + 9223372036854775808 + -9223372036854775807 * -2 }}
+
+Substractions roll over to INT64_MAX on underflow:
+{{ 0 - 9223372036854775809 }}
+-- End --
+
+-- Expect stdout --
+Unsigned additions roll over back to zero:
+0
+
+Unsigned multiplications roll over back to zero:
+0
+
+Signed additions roll over at INT64_MIN/INT64_MAX:
+9223372036854775807
+
+Signed multiplications roll over back to INT64_MIN:
+-9223372036854775807
+
+Multiplicating two negative operands yields an unsigned result.
+18446744073709551614
+
+Signed calculations yielding positive results are promoted to unsigned.
+18446744073709551614
+
+Substractions roll over to INT64_MAX on underflow:
+9223372036854775807
+-- End --
diff --git a/types.c b/types.c
index a8ca023..0122984 100644
--- a/types.c
+++ b/types.c
@@ -21,6 +21,7 @@
#include <errno.h>
#include <math.h>
#include <ctype.h>
+#include <float.h>
#include "ucode/types.h"
#include "ucode/util.h"
@@ -472,6 +473,7 @@ ucv_uint64_get(uc_value_t *uv)
{
uintptr_t pv = (uintptr_t)uv;
uc_integer_t *integer;
+ double d;
errno = 0;
@@ -483,7 +485,9 @@ ucv_uint64_get(uc_value_t *uv)
return 0;
}
- else if (uv != NULL && uv->type == UC_INTEGER) {
+
+ switch (ucv_type(uv)) {
+ case UC_INTEGER:
integer = (uc_integer_t *)uv;
if (integer->header.u64)
@@ -495,11 +499,29 @@ ucv_uint64_get(uc_value_t *uv)
errno = ERANGE;
return 0;
- }
- errno = EINVAL;
+ case UC_DOUBLE:
+ d = ucv_double_get(uv);
- return 0;
+ if (d < 0.0) {
+ errno = ERANGE;
+
+ return 0;
+ }
+
+ if (d >= ldexp(1.0, 64)) {
+ errno = ERANGE;
+
+ return UINT64_MAX;
+ }
+
+ return (uint64_t)d;
+
+ default:
+ errno = EINVAL;
+
+ return 0;
+ }
}
int64_t
@@ -507,6 +529,7 @@ ucv_int64_get(uc_value_t *uv)
{
uintptr_t pv = (uintptr_t)uv;
uc_integer_t *integer;
+ double d;
errno = 0;
@@ -516,10 +539,12 @@ ucv_int64_get(uc_value_t *uv)
return -(int64_t)(pv >> 3);
}
- else if (uv != NULL && uv->type == UC_INTEGER) {
+
+ switch (ucv_type(uv)) {
+ case UC_INTEGER:
integer = (uc_integer_t *)uv;
- if (integer->header.u64 && integer->i.u64 <= INT64_MAX)
+ if (integer->header.u64 && integer->i.u64 <= (uint64_t)INT64_MAX)
return (int64_t)integer->i.u64;
if (!integer->header.u64)
@@ -527,12 +552,30 @@ ucv_int64_get(uc_value_t *uv)
errno = ERANGE;
- return 0;
- }
+ return INT64_MAX;
+
+ case UC_DOUBLE:
+ d = ucv_double_get(uv);
- errno = EINVAL;
+ if (d < ldexp(-1.0, 63)) {
+ errno = ERANGE;
- return 0;
+ return INT64_MIN;
+ }
+
+ if (d >= ldexp(1.0, 63)) {
+ errno = ERANGE;
+
+ return INT64_MAX;
+ }
+
+ return (int64_t)d;
+
+ default:
+ errno = EINVAL;
+
+ return 0;
+ }
}
@@ -552,19 +595,43 @@ ucv_double_new(double d)
double
ucv_double_get(uc_value_t *uv)
{
+ uint64_t max_int = (2ULL << (DBL_MANT_DIG - 1));
uc_double_t *dbl;
+ uint64_t u;
+ int64_t n;
errno = 0;
- if (ucv_type(uv) != UC_DOUBLE) {
+ switch (ucv_type(uv)) {
+ case UC_DOUBLE:
+ dbl = (uc_double_t *)uv;
+
+ return dbl->dbl;
+
+ case UC_INTEGER:
+ n = ucv_int64_get(uv);
+
+ if (errno == ERANGE) {
+ u = ucv_uint64_get(uv);
+
+ /* signal precision loss for integral values >2^53 */
+ if (u > max_int)
+ errno = ERANGE;
+
+ return (double)u;
+ }
+
+ /* signal precision loss for integral values <-2^53 or >2^53 */
+ if (n < -(int64_t)max_int || n > (int64_t)max_int)
+ errno = ERANGE;
+
+ return (double)n;
+
+ default:
errno = EINVAL;
return NAN;
}
-
- dbl = (uc_double_t *)uv;
-
- return dbl->dbl;
}
@@ -1673,77 +1740,6 @@ ucv_to_jsonstring_formatted(uc_vm_t *vm, uc_value_t *uv, char pad_char, size_t p
return ucv_to_string_any(vm, uv, pad_char ? pad_char : '\1', pad_size);
}
-uc_type_t
-ucv_cast_number(uc_value_t *v, int64_t *n, double *d)
-{
- bool is_double = false;
- const char *s;
- char *e;
-
- *d = 0.0;
- *n = 0;
-
- switch (ucv_type(v)) {
- case UC_INTEGER:
- *n = ucv_int64_get(v);
-
- return UC_INTEGER;
-
- case UC_DOUBLE:
- *d = ucv_double_get(v);
-
- return UC_DOUBLE;
-
- case UC_NULL:
- return UC_INTEGER;
-
- case UC_BOOLEAN:
- *n = ucv_boolean_get(v);
-
- return UC_INTEGER;
-
- case UC_STRING:
- s = ucv_string_get(v);
-
- while (isspace(*s))
- s++;
-
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && isxdigit(s[2])) {
- *n = strtoll(s, &e, 16);
- }
- else if (s[0] == '0' && isdigit(s[2])) {
- *n = strtoll(s, &e, 8);
- }
- else {
- *n = strtoll(s, &e, 10);
-
- if (*e == '.') {
- *d = strtod(s, &e);
- is_double = (e > s);
- }
- }
-
- while (isspace(*e))
- e++;
-
- if (*e) {
- *d = NAN;
-
- return UC_DOUBLE;
- }
-
- if (is_double)
- return UC_DOUBLE;
-
- return UC_INTEGER;
-
- default:
- *d = NAN;
-
- return UC_DOUBLE;
- }
-}
-
bool
ucv_is_equal(uc_value_t *uv1, uc_value_t *uv2)
@@ -1874,33 +1870,78 @@ ucv_is_truish(uc_value_t *val)
}
}
+uc_value_t *
+ucv_to_number(uc_value_t *v)
+{
+ switch (ucv_type(v)) {
+ case UC_NULL:
+ return ucv_uint64_new(0);
+
+ case UC_BOOLEAN:
+ return ucv_uint64_new(ucv_boolean_get(v));
+
+ case UC_INTEGER:
+ return ucv_get(v);
+
+ case UC_DOUBLE:
+ if (isnan(ucv_double_get(v)))
+ return NULL;
+
+ return ucv_get(v);
+
+ case UC_STRING:
+ return uc_number_parse(ucv_string_get(v), NULL);
+
+ default:
+ return NULL;
+ }
+}
bool
-ucv_compare(int how, uc_value_t *v1, uc_value_t *v2)
+ucv_compare(int how, uc_value_t *v1, uc_value_t *v2, int *deltap)
{
uc_type_t t1 = ucv_type(v1);
uc_type_t t2 = ucv_type(v2);
- int64_t n1, n2, delta;
+ uc_value_t *nv1, *nv2;
+ uint64_t u1, u2;
+ int64_t n1, n2;
double d1, d2;
+ int8_t delta;
+ /* if both operands are strings, compare bytewise */
if (t1 == UC_STRING && t2 == UC_STRING) {
delta = strcmp(ucv_string_get(v1), ucv_string_get(v2));
}
+
+ /* handle non-string cases... */
else {
+ /* ... both operands are of the same, non-scalar type... */
if (t1 == t2 && !ucv_is_scalar(v1)) {
+ /* ... compare memory addrs */
delta = (intptr_t)v1 - (intptr_t)v2;
}
+
+ /* ... operands are of different type or at least one is scalar... */
else {
- t1 = ucv_cast_number(v1, &n1, &d1);
- t2 = ucv_cast_number(v2, &n2, &d2);
+ nv1 = ucv_to_number(v1);
+ nv2 = ucv_to_number(v2);
+
+ /* ... at least one of them is NaN (not convertible)... */
+ if (!nv1 || !nv2) {
+ ucv_put(nv1);
+ ucv_put(nv2);
- if (t1 == UC_DOUBLE || t2 == UC_DOUBLE) {
- d1 = (t1 == UC_DOUBLE) ? d1 : (double)n1;
- d2 = (t2 == UC_DOUBLE) ? d2 : (double)n2;
+ if (deltap)
+ *deltap = 2;
+
+ /* ... all comparison results except `!=` involving NaN are false */
+ return (how == I_NE);
+ }
- /* all comparison results except `!=` involving NaN are false */
- if (isnan(d1) || isnan(d2))
- return (how == I_NE);
+ /* ... either of them is a double, compare both as double */
+ if (ucv_type(nv1) == UC_DOUBLE || ucv_type(nv2) == UC_DOUBLE) {
+ d1 = ucv_double_get(nv1);
+ d2 = ucv_double_get(nv2);
if (d1 == d2)
delta = 0;
@@ -1909,12 +1950,67 @@ ucv_compare(int how, uc_value_t *v1, uc_value_t *v2)
else
delta = 1;
}
+
+ /* ... both are integers... */
else {
- delta = n1 - n2;
+ n1 = ucv_int64_get(nv1);
+
+ /* ... left operand is large positive... */
+ if (errno == ERANGE) {
+ ucv_int64_get(nv2);
+
+ /* ... right operand is large positive too... */
+ if (errno == ERANGE) {
+ /* ... compare both as unsigned */
+ u1 = ucv_uint64_get(nv1);
+ u2 = ucv_uint64_get(nv2);
+
+ if (u1 == u2)
+ delta = 0;
+ else if (u1 < u2)
+ delta = -1;
+ else
+ delta = 1;
+ }
+
+ /* ... right operand is within int64_t range... */
+ else {
+ /* ... left > right by definition */
+ delta = 1;
+ }
+ }
+
+ /* ... left operand is within int64_t range... */
+ else {
+ n2 = ucv_int64_get(nv2);
+
+ /* ... right operand is large positive... */
+ if (errno == ERANGE) {
+ /* ... left < right by definition */
+ delta = -1;
+ }
+
+ /* ... right operand is within int64_t range... */
+ else {
+ /* ... compare both as signed */
+ if (n1 == n2)
+ delta = 0;
+ else if (n1 < n2)
+ delta = -1;
+ else
+ delta = 1;
+ }
+ }
}
+
+ ucv_put(nv1);
+ ucv_put(nv2);
}
}
+ if (deltap)
+ *deltap = delta;
+
switch (how) {
case I_LT:
return (delta < 0);
@@ -1961,7 +2057,7 @@ ucv_key_to_index(uc_value_t *val)
if (ucv_type(val) == UC_DOUBLE) {
d = ucv_double_get(val);
- if ((double)(int64_t)(d) != d)
+ if (trunc(d) != d)
return INT64_MIN;
return (int64_t)d;
diff --git a/vallist.c b/vallist.c
index 6880f40..d6a8169 100644
--- a/vallist.c
+++ b/vallist.c
@@ -19,6 +19,7 @@
#include <math.h> /* isnan(), INFINITY */
#include <ctype.h> /* isspace(), isdigit(), isxdigit() */
#include <errno.h>
+#include <assert.h>
#include "ucode/util.h"
#include "ucode/chunk.h"
@@ -28,12 +29,11 @@
#define TAG_TYPE uint64_t
#define TAG_BITS 3
#define TAG_MASK ((1LL << ((sizeof(TAG_TYPE) << 3) - TAG_BITS)) - 1)
-#define TAG_MAXN (TAG_MASK / 2)
#define TAG_ALIGN(s) (((s) + (1 << TAG_BITS) - 1) & -(1 << TAG_BITS))
#define TAG_GET_TYPE(n) (int)((TAG_TYPE)n & ((1 << TAG_BITS) - 1))
-#define TAG_FIT_NV(n) ((int64_t)n >= -TAG_MAXN && (int64_t)n <= TAG_MAXN)
-#define TAG_SET_NV(n) ((TAG_TYPE)((int64_t)n + TAG_MAXN) << TAG_BITS)
-#define TAG_GET_NV(n) (int64_t)((int64_t)(((TAG_TYPE)n >> TAG_BITS) & TAG_MASK) - TAG_MAXN)
+#define TAG_FIT_NV(n) ((uint64_t)n <= TAG_MASK)
+#define TAG_SET_NV(n) ((TAG_TYPE)(uint64_t)n << TAG_BITS)
+#define TAG_GET_NV(n) (uint64_t)((uint64_t)((TAG_TYPE)n >> TAG_BITS) & TAG_MASK)
#define TAG_FIT_STR(l) ((l - 1) < (((sizeof(TAG_TYPE) << 3) - TAG_BITS) >> 3))
#define TAG_SET_STR_L(l) (TAG_TYPE)((l & ((1 << (8 - TAG_BITS)) - 1)) << TAG_BITS)
#define TAG_GET_STR_L(n) (size_t)(((TAG_TYPE)n >> TAG_BITS) & ((1 << (8 - TAG_BITS)) - 1))
@@ -43,6 +43,56 @@
#define UC_VALLIST_CHUNK_SIZE 8
+uc_value_t *
+uc_number_parse(const char *buf, char **end)
+{
+ unsigned long long u;
+ const char *p = buf;
+ bool neg = false;
+ double d;
+ char *e;
+
+ while (isspace(*p))
+ p++;
+
+ if (*p == '-') {
+ neg = true;
+ p++;
+ }
+
+ if (!isxdigit(*p))
+ return NULL;
+
+ if (!end)
+ end = &e;
+
+ u = strtoull(p, end, 0);
+
+ if (**end == '.' || **end == 'e' || **end == 'E') {
+ d = strtod(p, end);
+
+ if (*end == p || (!isspace(**end) && **end != 0))
+ return NULL;
+
+ if (neg)
+ d = -d;
+
+ return ucv_double_new(d);
+ }
+
+ if (*end == p || (!isspace(**end) && **end != 0))
+ return NULL;
+
+ if (neg) {
+ if (u > INT64_MAX)
+ return ucv_int64_new(INT64_MIN);
+
+ return ucv_int64_new(-(int64_t)u);
+ }
+
+ return ucv_uint64_new(u);
+}
+
void
uc_vallist_init(uc_value_list_t *list)
{
@@ -72,7 +122,7 @@ uc_vallist_free(uc_value_list_t *list)
}
static void
-add_num(uc_value_list_t *list, int64_t n)
+add_num(uc_value_list_t *list, uint64_t n)
{
size_t sz = TAG_ALIGN(sizeof(n));
@@ -97,7 +147,7 @@ add_num(uc_value_list_t *list, int64_t n)
}
static ssize_t
-find_num(uc_value_list_t *list, int64_t n)
+find_num(uc_value_list_t *list, uint64_t n)
{
TAG_TYPE search;
size_t i;
@@ -114,10 +164,10 @@ find_num(uc_value_list_t *list, int64_t n)
if (TAG_GET_TYPE(list->index[i]) != TAG_LNUM)
continue;
- if (TAG_GET_OFFSET(list->index[i]) + sizeof(int64_t) > list->dsize)
+ if (TAG_GET_OFFSET(list->index[i]) + sizeof(uint64_t) > list->dsize)
continue;
- if ((int64_t)be64toh(*(int64_t *)(list->data + TAG_GET_OFFSET(list->index[i]))) != n)
+ if ((uint64_t)be64toh(*(uint64_t *)(list->data + TAG_GET_OFFSET(list->index[i]))) != n)
continue;
return i;
@@ -278,6 +328,7 @@ ssize_t
uc_vallist_add(uc_value_list_t *list, uc_value_t *value)
{
ssize_t existing;
+ uint64_t u64;
if ((list->isize % UC_VALLIST_CHUNK_SIZE) == 0) {
list->index = xrealloc(list->index, sizeof(list->index[0]) * (list->isize + UC_VALLIST_CHUNK_SIZE));
@@ -286,13 +337,16 @@ uc_vallist_add(uc_value_list_t *list, uc_value_t *value)
switch (ucv_type(value)) {
case UC_INTEGER:
- /* XXX: u64 */
- existing = find_num(list, ucv_int64_get(value));
+ u64 = ucv_uint64_get(value);
+
+ assert(errno == 0);
+
+ existing = find_num(list, u64);
if (existing > -1)
return existing;
- add_num(list, ucv_int64_get(value));
+ add_num(list, u64);
break;
@@ -348,14 +402,13 @@ uc_vallist_get(uc_value_list_t *list, size_t idx)
switch (uc_vallist_type(list, idx)) {
case TAG_NUM:
- return ucv_int64_new(TAG_GET_NV(list->index[idx]));
+ return ucv_uint64_new(TAG_GET_NV(list->index[idx]));
case TAG_LNUM:
- if (TAG_GET_OFFSET(list->index[idx]) + sizeof(int64_t) > list->dsize)
+ if (TAG_GET_OFFSET(list->index[idx]) + sizeof(uint64_t) > list->dsize)
return NULL;
- /* XXX: u64 */
- return ucv_int64_new(be64toh(*(int64_t *)(list->data + TAG_GET_OFFSET(list->index[idx]))));
+ return ucv_uint64_new(be64toh(*(uint64_t *)(list->data + TAG_GET_OFFSET(list->index[idx]))));
case TAG_DBL:
if (TAG_GET_OFFSET(list->index[idx]) + sizeof(double) > list->dsize)
diff --git a/vm.c b/vm.c
index e21bc4b..abdb9b9 100644
--- a/vm.c
+++ b/vm.c
@@ -19,6 +19,8 @@
#include <assert.h>
#include <ctype.h>
#include <math.h>
+#include <errno.h>
+#include <limits.h>
#include "ucode/vm.h"
#include "ucode/compiler.h"
@@ -33,9 +35,9 @@ static const char *insn_names[__I_MAX] = {
static const int8_t insn_operand_bytes[__I_MAX] = {
[I_LOAD] = 4,
- [I_LOAD8] = -1,
- [I_LOAD16] = -2,
- [I_LOAD32] = -4,
+ [I_LOAD8] = 1,
+ [I_LOAD16] = 2,
+ [I_LOAD32] = 4,
[I_LREXP] = 4,
@@ -237,19 +239,6 @@ uc_vm_decode_insn(uc_vm_t *vm, uc_callframe_t *frame, uc_chunk_t *chunk)
case 0:
break;
- case -1:
- vm->arg.s8 = frame->ip[0] - 0x7f;
- frame->ip++;
- break;
-
- case -2:
- vm->arg.s16 = (
- frame->ip[0] * 0x100 +
- frame->ip[1]
- ) - 0x7fff;
- frame->ip += 2;
- break;
-
case -4:
vm->arg.s32 = (
frame->ip[0] * 0x1000000UL +
@@ -265,6 +254,14 @@ uc_vm_decode_insn(uc_vm_t *vm, uc_callframe_t *frame, uc_chunk_t *chunk)
frame->ip++;
break;
+ case 2:
+ vm->arg.u16 = (
+ frame->ip[0] * 0x100 +
+ frame->ip[1]
+ );
+ frame->ip += 2;
+ break;
+
case 4:
vm->arg.u32 = (
frame->ip[0] * 0x1000000UL +
@@ -922,15 +919,15 @@ uc_vm_insn_load(uc_vm_t *vm, uc_vm_insn_t insn)
break;
case I_LOAD8:
- uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s8));
+ uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u8));
break;
case I_LOAD16:
- uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s16));
+ uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u16));
break;
case I_LOAD32:
- uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s32));
+ uc_vm_stack_push(vm, ucv_uint64_new(vm->arg.u32));
break;
default:
@@ -1244,54 +1241,117 @@ uc_vm_insn_store_local(uc_vm_t *vm, uc_vm_insn_t insn)
uc_vm_stack_set(vm, frame->stackframe + vm->arg.u32, val);
}
+static int64_t
+int64(uc_value_t *nv, uint64_t *u64)
+{
+ int64_t n;
+
+ n = ucv_int64_get(nv);
+ *u64 = 0;
+
+ if (errno == ERANGE) {
+ n = INT64_MAX;
+ *u64 = ucv_uint64_get(nv);
+ }
+
+ return n;
+}
+
+static uint64_t
+abs64(int64_t n)
+{
+ if (n == INT64_MIN)
+ return 0x8000000000000000ULL;
+
+ if (n < 0)
+ return -n;
+
+ return n;
+}
+
+
static uc_value_t *
uc_vm_value_bitop(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_value_t *operand)
{
- uc_value_t *rv = NULL;
+ uc_value_t *nv1, *nv2, *rv = NULL;
+ uint64_t u1, u2;
int64_t n1, n2;
- double d;
- if (ucv_cast_number(value, &n1, &d) == UC_DOUBLE)
- n1 = isnan(d) ? 0 : (int64_t)d;
+ nv1 = ucv_to_number(value);
+ nv2 = ucv_to_number(operand);
- if (ucv_cast_number(operand, &n2, &d) == UC_DOUBLE)
- n2 = isnan(d) ? 0 : (int64_t)d;
+ n1 = int64(nv1, &u1);
+ n2 = int64(nv2, &u2);
- switch (operation) {
- case I_LSHIFT:
- rv = ucv_int64_new(n1 << n2);
- break;
+ if (n1 < 0 || n2 < 0) {
+ switch (operation) {
+ case I_LSHIFT:
+ rv = ucv_int64_new(n1 << n2);
+ break;
- case I_RSHIFT:
- rv = ucv_int64_new(n1 >> n2);
- break;
+ case I_RSHIFT:
+ rv = ucv_int64_new(n1 >> n2);
+ break;
- case I_BAND:
- rv = ucv_int64_new(n1 & n2);
- break;
+ case I_BAND:
+ rv = ucv_int64_new(n1 & n2);
+ break;
- case I_BXOR:
- rv = ucv_int64_new(n1 ^ n2);
- break;
+ case I_BXOR:
+ rv = ucv_int64_new(n1 ^ n2);
+ break;
- case I_BOR:
- rv = ucv_int64_new(n1 | n2);
- break;
+ case I_BOR:
+ rv = ucv_int64_new(n1 | n2);
+ break;
- default:
- break;
+ default:
+ break;
+ }
+ }
+ else {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ switch (operation) {
+ case I_LSHIFT:
+ rv = ucv_uint64_new(u1 << (u2 % (sizeof(uint64_t) * CHAR_BIT)));
+ break;
+
+ case I_RSHIFT:
+ rv = ucv_uint64_new(u1 >> (u2 % (sizeof(uint64_t) * CHAR_BIT)));
+ break;
+
+ case I_BAND:
+ rv = ucv_uint64_new(u1 & u2);
+ break;
+
+ case I_BXOR:
+ rv = ucv_uint64_new(u1 ^ u2);
+ break;
+
+ case I_BOR:
+ rv = ucv_uint64_new(u1 | u2);
+ break;
+
+ default:
+ break;
+ }
}
+ ucv_put(nv1);
+ ucv_put(nv2);
+
return rv;
}
static uc_value_t *
uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_value_t *operand)
{
- uc_value_t *rv = NULL;
- uc_type_t t1, t2;
+ uc_value_t *nv1, *nv2, *rv = NULL;
char *s, *s1, *s2;
size_t len1, len2;
+ uint64_t u1, u2;
int64_t n1, n2;
double d1, d2;
@@ -1318,12 +1378,19 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
return rv;
}
- t1 = ucv_cast_number(value, &n1, &d1);
- t2 = ucv_cast_number(operand, &n2, &d2);
+ nv1 = ucv_to_number(value);
+ nv2 = ucv_to_number(operand);
- if (t1 == UC_DOUBLE || t2 == UC_DOUBLE) {
- d1 = (t1 == UC_DOUBLE) ? d1 : (double)n1;
- d2 = (t2 == UC_DOUBLE) ? d2 : (double)n2;
+ /* any operation involving NaN results in NaN */
+ if (!nv1 || !nv2) {
+ ucv_put(nv1);
+ ucv_put(nv2);
+
+ return ucv_double_new(NAN);
+ }
+ if (ucv_type(nv1) == UC_DOUBLE || ucv_type(nv2) == UC_DOUBLE) {
+ d1 = ucv_double_get(nv1);
+ d2 = ucv_double_get(nv2);
switch (operation) {
case I_ADD:
@@ -1332,6 +1399,7 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
break;
case I_SUB:
+ case I_MINUS:
rv = ucv_double_new(d1 - d2);
break;
@@ -1352,7 +1420,7 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
break;
case I_MOD:
- rv = ucv_double_new(NAN);
+ rv = ucv_double_new(fmod(d1, d2));
break;
default:
@@ -1363,30 +1431,100 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
}
}
else {
+ n1 = int64(nv1, &u1);
+ n2 = int64(nv2, &u2);
+
switch (operation) {
case I_ADD:
case I_PLUS:
- rv = ucv_int64_new(n1 + n2);
+ if (n1 < 0 || n2 < 0) {
+ if (u1)
+ rv = ucv_uint64_new(u1 - abs64(n2));
+ else if (u2)
+ rv = ucv_uint64_new(u2 - abs64(n1));
+ else
+ rv = ucv_int64_new(n1 + n2);
+ }
+ else {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ rv = ucv_uint64_new(u1 + u2);
+ }
+
break;
case I_SUB:
- rv = ucv_int64_new(n1 - n2);
+ case I_MINUS:
+ if (n1 < 0 && n2 < 0) {
+ if (n1 > n2)
+ rv = ucv_uint64_new(abs64(n2) - abs64(n1));
+ else
+ rv = ucv_int64_new(n1 - n2);
+ }
+ else if (n1 >= 0 && n2 >= 0) {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ if (u2 > u1)
+ rv = ucv_int64_new(-(u2 - u1));
+ else
+ rv = ucv_uint64_new(u1 - u2);
+ }
+ else if (n1 >= 0) {
+ if (!u1) u1 = (uint64_t)n1;
+
+ rv = ucv_uint64_new(u1 + abs64(n2));
+ }
+ else {
+ rv = ucv_int64_new(n1 - n2);
+ }
+
break;
case I_MUL:
- rv = ucv_int64_new(n1 * n2);
+ if (n1 < 0 && n2 < 0) {
+ rv = ucv_uint64_new(abs64(n1) * abs64(n2));
+ }
+ else if (n1 >= 0 && n2 >= 0) {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ rv = ucv_uint64_new(u1 * u2);
+ }
+ else {
+ rv = ucv_int64_new(n1 * n2);
+ }
+
break;
case I_DIV:
- if (n2 == 0)
+ if (n2 == 0) {
rv = ucv_double_new(INFINITY);
- else
+ }
+ else if (n1 < 0 || n2 < 0) {
rv = ucv_int64_new(n1 / n2);
+ }
+ else {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ rv = ucv_uint64_new(u1 / u2);
+ }
break;
case I_MOD:
- rv = ucv_int64_new(n1 % n2);
+ if (n1 < 0 || n2 < 0) {
+ rv = ucv_int64_new(n1 % n2);
+ }
+ else {
+ if (!u1) u1 = (uint64_t)n1;
+ if (!u2) u2 = (uint64_t)n2;
+
+ rv = ucv_uint64_new(u1 % u2);
+ }
+
break;
default:
@@ -1397,6 +1535,9 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
}
}
+ ucv_put(nv1);
+ ucv_put(nv2);
+
return rv;
}
@@ -1643,25 +1784,71 @@ uc_vm_insn_arith(uc_vm_t *vm, uc_vm_insn_t insn)
static void
uc_vm_insn_plus_minus(uc_vm_t *vm, uc_vm_insn_t insn)
{
- uc_value_t *v = uc_vm_stack_pop(vm);
+ uc_value_t *v = uc_vm_stack_pop(vm), *nv;
bool is_sub = (insn == I_MINUS);
- uc_type_t t;
int64_t n;
double d;
- t = ucv_cast_number(v, &n, &d);
+ if (ucv_type(v) == UC_STRING) {
+ nv = uc_number_parse(ucv_string_get(v), NULL);
- ucv_put(v);
+ if (nv) {
+ ucv_put(v);
+ v = nv;
+ }
+ }
- switch (t) {
+ switch (ucv_type(v)) {
case UC_INTEGER:
- uc_vm_stack_push(vm, ucv_int64_new(is_sub ? -n : n));
+ n = ucv_int64_get(v);
+
+ /* numeric value is in range 9223372036854775808..18446744073709551615 */
+ if (errno == ERANGE) {
+ if (is_sub)
+ /* make negation of large numeric value result in smallest negative value */
+ uc_vm_stack_push(vm, ucv_int64_new(INT64_MIN));
+ else
+ /* for positive number coercion return value as-is */
+ uc_vm_stack_push(vm, ucv_get(v));
+ }
+
+ /* numeric value is in range -9223372036854775808..9223372036854775807 */
+ else {
+ if (is_sub) {
+ if (n == INT64_MIN)
+ /* make negation of minimum value result in maximum signed positive value */
+ uc_vm_stack_push(vm, ucv_int64_new(INT64_MAX));
+ else
+ /* for all other values flip the sign */
+ uc_vm_stack_push(vm, ucv_int64_new(-n));
+ }
+ else {
+ /* for positive number coercion return value as-is */
+ uc_vm_stack_push(vm, ucv_get(v));
+ }
+ }
+
break;
- default:
+ case UC_DOUBLE:
+ d = ucv_double_get(v);
uc_vm_stack_push(vm, ucv_double_new(is_sub ? -d : d));
break;
+
+ case UC_BOOLEAN:
+ n = (int64_t)ucv_boolean_get(v);
+ uc_vm_stack_push(vm, ucv_int64_new(is_sub ? -n : n));
+ break;
+
+ case UC_NULL:
+ uc_vm_stack_push(vm, ucv_int64_new(0));
+ break;
+
+ default:
+ uc_vm_stack_push(vm, ucv_double_new(NAN));
}
+
+ ucv_put(v);
}
static void
@@ -1683,15 +1870,24 @@ static void
uc_vm_insn_complement(uc_vm_t *vm, uc_vm_insn_t insn)
{
uc_value_t *v = uc_vm_stack_pop(vm);
+ uc_value_t *nv;
+ uint64_t u;
int64_t n;
- double d;
- if (ucv_cast_number(v, &n, &d) == UC_DOUBLE)
- n = isnan(d) ? 0 : (int64_t)d;
+ nv = ucv_to_number(v);
+ n = int64(nv, &u);
- ucv_put(v);
+ if (n < 0) {
+ uc_vm_stack_push(vm, ucv_int64_new(~n));
+ }
+ else {
+ if (!u) u = (uint64_t)n;
+
+ uc_vm_stack_push(vm, ucv_uint64_new(~u));
+ }
- uc_vm_stack_push(vm, ucv_int64_new(~n));
+ ucv_put(nv);
+ ucv_put(v);
}
static void
@@ -1700,7 +1896,7 @@ uc_vm_insn_rel(uc_vm_t *vm, uc_vm_insn_t insn)
uc_value_t *r2 = uc_vm_stack_pop(vm);
uc_value_t *r1 = uc_vm_stack_pop(vm);
- bool res = ucv_compare(insn, r1, r2);
+ bool res = ucv_compare(insn, r1, r2, NULL);
ucv_put(r1);
ucv_put(r2);
@@ -1724,7 +1920,7 @@ uc_vm_insn_in(uc_vm_t *vm, uc_vm_insn_t insn)
arridx < arrlen; arridx++) {
item = ucv_array_get(r2, arridx);
- if (ucv_compare(I_EQ, r1, item)) {
+ if (ucv_compare(I_EQ, r1, item, NULL)) {
found = true;
break;
}