[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