[njs] Added support of hex literals.

Dmitry Volyntsev xeioex at nginx.com
Tue May 30 18:02:33 UTC 2017


details:   http://hg.nginx.org/njs/rev/a38c33e9f728
branches:  
changeset: 347:a38c33e9f728
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue May 30 19:35:08 2017 +0300
description:
Added support of hex literals.

diffstat:

 njs/njs_lexer.c          |   57 ++++----------------
 njs/njs_number.c         |   87 ++++++++++++++++++++++----------
 njs/njs_number.h         |    5 +-
 njs/njs_parser.c         |   18 +++---
 njs/njs_string.c         |   64 ++++++++++++++++++++---
 njs/njs_string.h         |    3 +-
 njs/njs_vm.c             |   31 +++-------
 njs/test/njs_unit_test.c |  124 +++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 275 insertions(+), 114 deletions(-)

diffs (682 lines):

diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_lexer.c
--- a/njs/njs_lexer.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_lexer.c	Tue May 30 19:35:08 2017 +0300
@@ -16,6 +16,7 @@
 #include <nxt_mem_cache_pool.h>
 #include <njscript.h>
 #include <njs_vm.h>
+#include <njs_number.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
 #include <string.h>
@@ -538,59 +539,27 @@ static njs_token_t
 njs_lexer_number(njs_lexer_t *lexer)
 {
     u_char  c, *p;
-    double  num, frac, scale;
-
-    /* TODO: "1e2" */
 
     p = lexer->start;
     c = p[-1];
 
-    /* Values below '0' become >= 208. */
-    c = c - '0';
-
-    num = c;
-
-    if (c != 0) {
-
-        while (p < lexer->end) {
-            c = *p;
+    /* Hexadecimal literal values. */
 
-            /* Values below '0' become >= 208. */
-            c = c - '0';
+    if (c == '0' && p != lexer->end && (*p == 'x' || *p == 'X')) {
+        p++;
 
-            if (nxt_slow_path(c > 9)) {
-                break;
-            }
+        if (p == lexer->end) {
+            return NJS_TOKEN_ILLEGAL;
+        }
 
-            num = num * 10 + c;
-            p++;
-        }
+        lexer->start = p;
+        lexer->number = njs_number_hex_parse(&lexer->start, lexer->end);
+
+        return NJS_TOKEN_NUMBER;
     }
 
-    if (*p == '.') {
-
-        frac = 0;
-        scale = 1;
-
-        for (p++; p < lexer->end; p++) {
-            c = *p;
-
-            /* Values below '0' become >= 208. */
-            c = c - '0';
-
-            if (nxt_slow_path(c > 9)) {
-                break;
-            }
-
-            frac = frac * 10 + c;
-            scale *= 10;
-        }
-
-        num += frac / scale;
-    }
-
-    lexer->number = num;
-    lexer->start = p;
+    lexer->start = p - 1;
+    lexer->number = njs_number_dec_parse(&lexer->start, lexer->end);
 
     return NJS_TOKEN_NUMBER;
 }
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_number.c
--- a/njs/njs_number.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_number.c	Tue May 30 19:35:08 2017 +0300
@@ -37,20 +37,21 @@ static njs_ret_t njs_number_to_string_ra
     double number, uint32_t radix);
 
 
-double
-njs_value_to_number(njs_value_t *value)
+uint32_t
+njs_value_to_index(njs_value_t *value)
 {
+    double       num;
     njs_array_t  *array;
 
+    num = NAN;
+
     if (nxt_fast_path(njs_is_numeric(value))) {
-        return value->data.u.number;
-    }
+        num = value->data.u.number;
 
-    if (njs_is_string(value)) {
-        return njs_string_to_number(value, 1);
-    }
+    } else if (njs_is_string(value)) {
+        num = njs_string_to_index(value);
 
-    if (njs_is_array(value)) {
+    } else if (njs_is_array(value)) {
 
         array = value->data.u.array;
 
@@ -58,42 +59,39 @@ njs_value_to_number(njs_value_t *value)
 
             if (array->length == 0) {
                 /* An empty array value is zero. */
-                return 0.0;
+                return 0;
             }
 
             if (array->length == 1 && njs_is_valid(&array->start[0])) {
                 /* A single value array is the zeroth array value. */
-                return njs_value_to_number(&array->start[0]);
+                return njs_value_to_index(&array->start[0]);
             }
         }
     }
 
-    return NAN;
+    if ((uint32_t) num == num) {
+        return (uint32_t) num;
+    }
+
+    return NJS_ARRAY_INVALID_INDEX;
 }
 
 
 double
-njs_number_parse(const u_char **start, const u_char *end)
+njs_number_dec_parse(u_char **start, u_char *end)
 {
-    u_char        c;
-    double        num, frac, scale;
-    const u_char  *p;
+    u_char  c, *p;
+    double  num, frac, scale;
 
     /* TODO: "1e2" */
 
     p = *start;
-    c = *p++;
 
-    /* Values below '0' become >= 208. */
-    c = c - '0';
-
-    num = c;
+    num = 0;
 
     while (p < end) {
-        c = *p;
-
         /* Values below '0' become >= 208. */
-        c = c - '0';
+        c = *p - '0';
 
         if (nxt_slow_path(c > 9)) {
             break;
@@ -109,10 +107,8 @@ njs_number_parse(const u_char **start, c
         scale = 1;
 
         for (p++; p < end; p++) {
-            c = *p;
-
             /* Values below '0' become >= 208. */
-            c = c - '0';
+            c = *p - '0';
 
             if (nxt_slow_path(c > 9)) {
                 break;
@@ -131,6 +127,43 @@ njs_number_parse(const u_char **start, c
 }
 
 
+uint64_t
+njs_number_hex_parse(u_char **start, u_char *end)
+{
+    u_char    c, *p;
+    uint64_t  num;
+
+    p = *start;
+
+    num = 0;
+
+    while (p < end) {
+        c = (u_char) (*p | 0x20);
+
+        /* Values below '0' become >= 208. */
+        c = c - '0';
+
+        if (c > 9) {
+            /* Values below 'a' become >= 159. */
+            c = c - ('a' - '0');
+
+            if (nxt_slow_path(c > 5)) {
+                break;
+            }
+
+            c += 10;
+        }
+
+        num = num * 16 + c;
+        p++;
+    }
+
+    *start = p;
+
+    return num;
+}
+
+
 int64_t
 njs_number_radix_parse(u_char **start, u_char *end, uint8_t radix)
 {
@@ -745,7 +778,7 @@ njs_number_parse_float(njs_vm_t *vm, njs
     num = NAN;
 
     if (nargs > 1) {
-        num = njs_string_to_number(&args[1], 0);
+        num = njs_string_to_number(&args[1], 1);
     }
 
     njs_number_set(&vm->retval, num);
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_number.h
--- a/njs/njs_number.h	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_number.h	Tue May 30 19:35:08 2017 +0300
@@ -11,8 +11,9 @@
 #include <math.h>
 
 
-double njs_value_to_number(njs_value_t *value);
-double njs_number_parse(const u_char **start, const u_char *end);
+uint32_t njs_value_to_index(njs_value_t *value);
+double njs_number_dec_parse(u_char **start, u_char *end);
+uint64_t njs_number_hex_parse(u_char **start, u_char *end);
 int64_t njs_number_radix_parse(u_char **start, u_char *end, uint8_t radix);
 njs_ret_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
     const njs_value_t *number);
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_parser.c
--- a/njs/njs_parser.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_parser.c	Tue May 30 19:35:08 2017 +0300
@@ -2302,9 +2302,9 @@ static njs_token_t
 njs_parser_escape_string_create(njs_vm_t *vm, njs_parser_t *parser,
     njs_value_t *value)
 {
-    u_char   c, *p, *start, *dst, *src, *end, *hex_end;
-    size_t   size, length, hex_length;
-    int64_t  u;
+    u_char    c, *p, *start, *dst, *src, *end, *hex_end;
+    size_t    size, length, hex_length;
+    uint64_t  u;
 
     start = NULL;
     dst = NULL;
@@ -2422,11 +2422,7 @@ njs_parser_escape_string_create(njs_vm_t
         hex:
 
             p = src;
-            u = njs_number_radix_parse(&src, hex_end, 16);
-
-            if (nxt_slow_path(u < 0)) {
-                goto invalid;
-            }
+            u = njs_number_hex_parse(&src, hex_end);
 
             if (hex_length != 0) {
                 if (src != hex_end) {
@@ -2434,7 +2430,11 @@ njs_parser_escape_string_create(njs_vm_t
                 }
 
             } else {
-                if ((src - p) > 6 || src == end || *src++ != '}') {
+                if (src == p || (src - p) > 6) {
+                    goto invalid;
+                }
+
+                if (src == end || *src++ != '}') {
                     goto invalid;
                 }
             }
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_string.c
--- a/njs/njs_string.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_string.c	Tue May 30 19:35:08 2017 +0300
@@ -2943,12 +2943,12 @@ njs_primitive_value_to_string(njs_vm_t *
 
 
 double
-njs_string_to_number(njs_value_t *value, nxt_bool_t exact)
+njs_string_to_number(njs_value_t *value, nxt_bool_t parse_float)
 {
+    u_char        *p, *start, *end;
     double        num;
     size_t        size;
     nxt_bool_t    minus;
-    const u_char  *p, *end;
 
     const size_t  infinity = sizeof("Infinity") - 1;
 
@@ -2990,19 +2990,27 @@ njs_string_to_number(njs_value_t *value,
         return NAN;
     }
 
-    if (*p >= '0' && *p <= '9') {
-        num = njs_number_parse(&p, end);
+    if (!parse_float
+        && p + 2 < end && p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+    {
+        p += 2;
+        num = njs_number_hex_parse(&p, end);
 
     } else {
-        if (p + infinity > end || memcmp(p, "Infinity", infinity) != 0) {
-            return NAN;
+        start = p;
+        num = njs_number_dec_parse(&p, end);
+
+        if (p == start) {
+            if (p + infinity > end || memcmp(p, "Infinity", infinity) != 0) {
+                return NAN;
+            }
+
+            num = INFINITY;
+            p += infinity;
         }
-
-        num = INFINITY;
-        p += infinity;
     }
 
-    if (exact) {
+    if (!parse_float) {
         while (p < end) {
             if (*p != ' ' && *p != '\t') {
                 return NAN;
@@ -3016,6 +3024,42 @@ njs_string_to_number(njs_value_t *value,
 }
 
 
+double
+njs_string_to_index(njs_value_t *value)
+{
+    u_char  *p, *end;
+    double  num;
+    size_t  size;
+
+    size = value->short_string.size;
+
+    if (size != NJS_STRING_LONG) {
+        p = value->short_string.start;
+
+    } else {
+        size = value->data.string_size;
+        p = value->data.u.string->start;
+    }
+
+    if (size == 0) {
+        return NAN;
+    }
+
+    if (*p == '0' && size > 1) {
+        return NAN;
+    }
+
+    end = p + size;
+    num = njs_number_dec_parse(&p, end);
+
+    if (p != end) {
+        return NAN;
+    }
+
+    return num;
+}
+
+
 static const njs_object_prop_t  njs_string_prototype_properties[] =
 {
     {
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_string.h
--- a/njs/njs_string.h	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_string.h	Tue May 30 19:35:08 2017 +0300
@@ -142,7 +142,8 @@ nxt_noinline uint32_t njs_string_index(n
 void njs_string_offset_map_init(const u_char *start, size_t size);
 njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *src);
-double njs_string_to_number(njs_value_t *value, nxt_bool_t exact);
+double njs_string_to_index(njs_value_t *value);
+double njs_string_to_number(njs_value_t *value, nxt_bool_t parse_float);
 njs_ret_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 njs_ret_t njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args,
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/njs_vm.c
--- a/njs/njs_vm.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/njs_vm.c	Tue May 30 19:35:08 2017 +0300
@@ -509,7 +509,6 @@ njs_ret_t
 njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *property)
 {
-    double                num;
     int32_t               index;
     uintptr_t             data;
     njs_ret_t             ret;
@@ -576,10 +575,9 @@ njs_vmcode_property_get(njs_vm_t *vm, nj
 
         /* string[n]. */
 
-        num = njs_value_to_number(property);
-        index = (int32_t) num;
-
-        if (index >= 0 && index == num) {
+        index = (int32_t) njs_value_to_index(property);
+
+        if (nxt_fast_path(index >= 0)) {
             slice.start = index;
             slice.length = 1;
             slice.string_length = njs_string_prop(&string, object);
@@ -943,11 +941,9 @@ static nxt_noinline njs_ret_t
 njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     njs_value_t *property)
 {
-    double          num;
     uint32_t        index;
     uint32_t        (*hash)(const void *, size_t);
     njs_ret_t       ret;
-    nxt_bool_t      valid;
     njs_extern_t    *ext;
     njs_object_t    *obj;
     njs_function_t  *function;
@@ -978,22 +974,15 @@ njs_property_query(njs_vm_t *vm, njs_pro
         if (nxt_fast_path(!njs_is_null_or_void_or_boolean(property))) {
 
             if (nxt_fast_path(njs_is_primitive(property))) {
-                num = njs_value_to_number(property);
+                index = njs_value_to_index(property);
+
+                if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
+                    return njs_array_property_query(vm, pq, object, index);
+                }
 
             } else {
                 return NJS_TRAP_PROPERTY;
             }
-
-            if (nxt_fast_path(num >= 0)) {
-                index = (uint32_t) num;
-
-                valid = nxt_expect(1, (index < NJS_ARRAY_MAX_LENGTH
-                                       && (double) index == num));
-
-                if (valid) {
-                    return njs_array_property_query(vm, pq, object, index);
-                }
-            }
         }
 
         /* Fall through. */
@@ -3026,7 +3015,7 @@ njs_vmcode_number_primitive(njs_vm_t *vm
             num = NAN;
 
             if (njs_is_string(value)) {
-                num = njs_string_to_number(value, 1);
+                num = njs_string_to_number(value, 0);
             }
 
             njs_number_set(value, num);
@@ -3079,7 +3068,7 @@ njs_vmcode_number_argument(njs_vm_t *vm,
             num = NAN;
 
             if (njs_is_string(value)) {
-                num = njs_string_to_number(value, 1);
+                num = njs_string_to_number(value, 0);
             }
 
             njs_number_set(value, num);
diff -r 4e2da602c2a3 -r a38c33e9f728 njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Mon May 29 22:13:21 2017 +0300
+++ b/njs/test/njs_unit_test.c	Tue May 30 19:35:08 2017 +0300
@@ -115,6 +115,29 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("+1\n"),
       nxt_string("1") },
 
+    /* Hex Numbers. */
+
+    { nxt_string("0x0"),
+      nxt_string("0") },
+
+    { nxt_string("-0x1"),
+      nxt_string("-1") },
+
+    { nxt_string("0xffFF"),
+      nxt_string("65535") },
+
+    { nxt_string("0X0000BEEF"),
+      nxt_string("48879") },
+
+    { nxt_string("0x"),
+      nxt_string("SyntaxError: Unexpected token \"\" in 1") },
+
+    { nxt_string("0xffff."),
+      nxt_string("SyntaxError: Unexpected token \"\" in 1") },
+
+    { nxt_string("0x12g"),
+      nxt_string("SyntaxError: Unexpected token \"g\" in 1") },
+
     { nxt_string(""),
       nxt_string("undefined") },
 
@@ -127,6 +150,17 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("\n +1"),
       nxt_string("1") },
 
+    /* Indexes. */
+
+    { nxt_string("var a = []; a[-1] = 2; a[-1] == a['-1']"),
+      nxt_string("true") },
+
+    { nxt_string("var a = []; a[Infinity] = 2; a[Infinity] == a['Infinity']"),
+      nxt_string("true") },
+
+    { nxt_string("var a = []; a[NaN] = 2; a[NaN] == a['NaN']"),
+      nxt_string("true") },
+
     /* Number.toString(radix) method. */
 
     { nxt_string("0..toString(2)"),
@@ -153,6 +187,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("81985529216486895..toString(16)"),
       nxt_string("123456789abcdf0") },
 
+    { nxt_string("0xffff.toString(16)"),
+      nxt_string("ffff") },
+
     { nxt_string("1845449130881..toString(36)"),
       nxt_string("njscript") },
 
@@ -220,12 +257,18 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("1 + ''"),
       nxt_string("1") },
 
+    { nxt_string("0xA + ''"),
+      nxt_string("10") },
+
     { nxt_string("undefined + undefined"),
       nxt_string("NaN") },
 
     { nxt_string("1.2 + 5.7"),
       nxt_string("6.9") },
 
+    { nxt_string("0xf + 1"),
+      nxt_string("16") },
+
     { nxt_string("1 + 1 + '2' + 1 + 1"),
       nxt_string("2211") },
 
@@ -235,6 +278,30 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("1.2 + -'5.7'"),
       nxt_string("-4.5") },
 
+    { nxt_string("1.2 - '-5.7'"),
+      nxt_string("6.9") },
+
+    { nxt_string("5 - ' \t 12  \t'"),
+      nxt_string("-7") },
+
+    { nxt_string("5 - '12zz'"),
+      nxt_string("NaN") },
+
+    { nxt_string("5 - '0x2'"),
+      nxt_string("3") },
+
+    { nxt_string("5 - '-0x2'"),
+      nxt_string("7") },
+
+    { nxt_string("5 - '\t 0x2 \t'"),
+      nxt_string("3") },
+
+    { nxt_string("5 - '0x2 z'"),
+      nxt_string("NaN") },
+
+    { nxt_string("5 - '0x'"),
+      nxt_string("NaN") },
+
     { nxt_string("1 + +'3'"),
       nxt_string("4") },
 
@@ -2334,6 +2401,18 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"),
       nxt_string("1 2 3 undefined") },
 
+    { nxt_string("var a = [ 5, 6, 7 ]; a['1']"),
+      nxt_string("6") },
+
+    { nxt_string("var a = [ 5, 6, 7 ]; a['01']"),
+      nxt_string("undefined") },
+
+    { nxt_string("var a = [ 5, 6, 7 ]; a[0x1]"),
+      nxt_string("6") },
+
+    { nxt_string("var a = [ 5, 6, 7 ]; a['0x1']"),
+      nxt_string("undefined") },
+
     { nxt_string("[] - 2"),
       nxt_string("-2") },
 
@@ -3145,6 +3224,12 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'\\u03B'"),
       nxt_string("SyntaxError: Invalid Unicode code point \"\\u03B\" in 1") },
 
+    { nxt_string("'\\u03BG'"),
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u03BG\" in 1") },
+
+    { nxt_string("'\\u03B '"),
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u03B \" in 1") },
+
     { nxt_string("'\\u{61}\\u{3B1}\\u{20AC}'"),
       nxt_string("aα€") },
 
@@ -3449,6 +3534,30 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abcdef'[8]"),
       nxt_string("undefined") },
 
+    { nxt_string("'abcdef'['1']"),
+      nxt_string("b") },
+
+    { nxt_string("'abcdef'[' 1']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['1 ']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['-']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['-1']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['01']"),
+      nxt_string("undefined") },
+
+    { nxt_string("'abcdef'['0x01']"),
+      nxt_string("undefined") },
+
     { nxt_string("var a = 'abcdef', b = 1 + 2; a[b]"),
       nxt_string("d") },
 
@@ -7077,6 +7186,21 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("parseFloat('12345abc')"),
       nxt_string("12345") },
 
+    { nxt_string("parseFloat('0x')"),
+      nxt_string("0") },
+
+    { nxt_string("parseFloat('0xff')"),
+      nxt_string("0") },
+
+    { nxt_string("parseFloat('Infinity')"),
+      nxt_string("Infinity") },
+
+    { nxt_string("parseFloat(' Infinityzz')"),
+      nxt_string("Infinity") },
+
+    { nxt_string("parseFloat('Infinit')"),
+      nxt_string("NaN") },
+
     /* Trick: number to boolean. */
 
     { nxt_string("var a = 0; !!a"),


More information about the nginx-devel mailing list