diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-01-04 16:16:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-04 16:16:28 +0100 |
commit | 1377e23afff90128b18ac60c10071295fc0afbab (patch) | |
tree | 04397dab9be96a5978e08366299671a8aa507267 | |
parent | 8907ce41a36f8d42097d884550fb3cfbba62e6c5 (diff) | |
parent | b605dbfcf04f310e08634b52507da7a4155bfce1 (diff) |
Merge pull request #30 from jow-/rework-number-handling
treewide: rework numeric value handling
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | compiler.c | 55 | ||||
-rw-r--r-- | include/ucode/types.h | 32 | ||||
-rw-r--r-- | include/ucode/vallist.h | 2 | ||||
-rw-r--r-- | lexer.c | 40 | ||||
-rw-r--r-- | lib.c | 158 | ||||
-rw-r--r-- | lib/math.c | 40 | ||||
-rw-r--r-- | lib/nl80211.c | 74 | ||||
-rw-r--r-- | lib/rtnl.c | 74 | ||||
-rw-r--r-- | tests/custom/00_syntax/10_numeric_literals | 6 | ||||
-rw-r--r-- | tests/custom/01_arithmetic/02_modulo | 23 | ||||
-rw-r--r-- | tests/custom/01_arithmetic/03_bitwise | 10 | ||||
-rw-r--r-- | tests/custom/01_arithmetic/05_overflow | 54 | ||||
-rw-r--r-- | types.c | 292 | ||||
-rw-r--r-- | vallist.c | 83 | ||||
-rw-r--r-- | vm.c | 338 |
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) @@ -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); @@ -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")); } @@ -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; } @@ -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 @@ -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 -- @@ -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; @@ -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) @@ -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; } |