From 6bc2bf1b4e7d9b53cd18e100135dece0ae9ff81c Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Wed, 23 Mar 2016 15:27:14 +0300 Subject: [PATCH] Math object. --- Makefile | 14 + njs/njs_array.c | 1 + njs/njs_builtin.c | 22 +- njs/njs_disassembler.c | 4 +- njs/njs_function.c | 15 +- njs/njs_generator.c | 42 ++- njs/njs_lexer_keyword.c | 1 + njs/njs_math.c | 554 +++++++++++++++++++++++++++++++++++++++ njs/njs_math.h | 17 ++ njs/njs_object.c | 28 +- njs/njs_object.h | 2 + njs/njs_parser.c | 37 ++- njs/njs_parser.h | 4 + njs/njs_regexp.c | 1 + njs/njs_vm.c | 64 +++-- njs/njs_vm.h | 26 +- njs/test/njs_unit_test.c | 62 +++++ 17 files changed, 841 insertions(+), 53 deletions(-) create mode 100644 njs/njs_math.c create mode 100644 njs/njs_math.h diff --git a/Makefile b/Makefile index d13489a1..168795b3 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_BUILDDIR)/njs_array.o \ $(NXT_BUILDDIR)/njs_function.o \ $(NXT_BUILDDIR)/njs_regexp.o \ + $(NXT_BUILDDIR)/njs_math.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ $(NXT_BUILDDIR)/njs_builtin.o \ @@ -46,6 +47,7 @@ $(NXT_BUILDDIR)/libnjs.a: \ $(NXT_BUILDDIR)/njs_array.o \ $(NXT_BUILDDIR)/njs_function.o \ $(NXT_BUILDDIR)/njs_regexp.o \ + $(NXT_BUILDDIR)/njs_math.o \ $(NXT_BUILDDIR)/njs_extern.o \ $(NXT_BUILDDIR)/njs_variable.o \ $(NXT_BUILDDIR)/njs_builtin.o \ @@ -220,6 +222,18 @@ $(NXT_BUILDDIR)/njs_regexp.o: \ -I$(NXT_LIB) -Injs $(NXT_PCRE_CFLAGS) \ njs/njs_regexp.c +$(NXT_BUILDDIR)/njs_math.o: \ + $(NXT_BUILDDIR)/libnxt.a \ + njs/njscript.h \ + njs/njs_vm.h \ + njs/njs_object.h \ + njs/njs_math.h \ + njs/njs_math.c \ + + $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_math.o $(NXT_CFLAGS) \ + -I$(NXT_LIB) -Injs \ + njs/njs_math.c + $(NXT_BUILDDIR)/njs_extern.o: \ $(NXT_BUILDDIR)/libnxt.a \ njs/njscript.h \ diff --git a/njs/njs_array.c b/njs/njs_array.c index ffed79ae..35d4b01b 100644 --- a/njs/njs_array.c +++ b/njs/njs_array.c @@ -129,6 +129,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare) nxt_lvlhsh_init(&array->object.hash); nxt_lvlhsh_init(&array->object.shared_hash); array->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_ARRAY]; + array->object.shared = 0; array->size = size; array->length = length; diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index d50953ea..965d4559 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -33,7 +34,7 @@ njs_builtin_objects_create(njs_vm_t *vm) { nxt_int_t ret; nxt_uint_t i; - njs_object_t *prototypes; + njs_object_t *objects, *prototypes; njs_function_t *functions; static const njs_object_init_t *prototype_init[] = { @@ -71,6 +72,10 @@ njs_builtin_objects_create(njs_vm_t *vm) { njs_eval_function, { 0 } }, }; + static const njs_object_init_t *objects_init[] = { + &njs_math_object_init, + }; + static const njs_object_prop_t null_proto_property = { .type = NJS_WHITEOUT, .name = njs_string("__proto__"), @@ -83,6 +88,19 @@ njs_builtin_objects_create(njs_vm_t *vm) return NXT_ERROR; } + objects = vm->shared->objects; + + for (i = NJS_OBJECT_MATH; i < NJS_OBJECT_MAX; i++) { + ret = njs_object_hash_create(vm, &objects[i].shared_hash, + objects_init[i]->properties, + objects_init[i]->items); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + objects[i].shared = 1; + } + prototypes = vm->shared->prototypes; for (i = NJS_PROTOTYPE_OBJECT; i < NJS_PROTOTYPE_MAX; i++) { @@ -99,9 +117,9 @@ njs_builtin_objects_create(njs_vm_t *vm) functions = vm->shared->functions; for (i = NJS_FUNCTION_OBJECT; i < NJS_FUNCTION_MAX; i++) { + functions[i].object.shared = 0; functions[i].native = 1; functions[i].args_offset = 1; - functions[i].shared = 0; functions[i].u.native = native_functions[i].native; functions[i].args_types[0] = native_functions[i].args_types[0]; functions[i].args_types[1] = native_functions[i].args_types[1]; diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index ef4893b7..a75608d3 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -35,10 +35,10 @@ static njs_code_name_t code_names[] = { nxt_string("OBJECT ") }, { njs_vmcode_function, sizeof(njs_vmcode_function_t), nxt_string("FUNCTION ") }, - { njs_vmcode_function_copy, sizeof(njs_vmcode_function_copy_t), - nxt_string("FUNCTION COPY ") }, { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t), nxt_string("REGEXP ") }, + { njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t), + nxt_string("OBJECT COPY ") }, { njs_vmcode_property_get, sizeof(njs_vmcode_prop_get_t), nxt_string("PROPERTY GET ") }, diff --git a/njs/njs_function.c b/njs/njs_function.c index 7586133a..058da84f 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -31,8 +31,15 @@ njs_function_alloc(njs_vm_t *vm) function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t)); if (nxt_fast_path(function != NULL)) { + /* + * nxt_mem_cache_zalloc() does also: + * nxt_lvlhsh_init(&function->object.hash); + * nxt_lvlhsh_init(&function->object.shared_hash); + * function->object.__proto__ = NULL; + */ + + function->object.shared = 1; function->args_offset = 1; - function->shared = 1; function->u.lambda = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_lambda_t)); @@ -52,7 +59,7 @@ njs_function_value_copy(njs_vm_t *vm, njs_value_t *value) function = value->data.u.function; - if (!function->shared) { + if (!function->object.shared) { return function; } @@ -61,7 +68,7 @@ njs_function_value_copy(njs_vm_t *vm, njs_value_t *value) if (nxt_fast_path(function != NULL)) { *function = *value->data.u.function; function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; - function->shared = 0; + function->object.shared = 0; value->data.u.function = function; } @@ -458,7 +465,7 @@ njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, *function = *args[0].data.u.function; function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION]; - function->shared = 0; + function->object.shared = 0; if (nargs == 1) { args = (njs_value_t *) &njs_value_void; diff --git a/njs/njs_generator.c b/njs/njs_generator.c index c6eb141a..2cbfa542 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -25,6 +25,8 @@ static nxt_int_t njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node); +static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser, + njs_parser_node_t *node); static nxt_int_t njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node); static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser, @@ -289,6 +291,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) case NJS_TOKEN_NAME: return njs_generate_name(vm, parser, node); + case NJS_TOKEN_MATH: + return njs_generate_builtin_object(vm, parser, node); + case NJS_TOKEN_FUNCTION: return njs_generate_function_declaration(vm, parser, node); @@ -319,9 +324,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) static nxt_int_t njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) { - njs_index_t index; - njs_value_t *value; - njs_vmcode_function_copy_t *copy; + njs_index_t index; + njs_value_t *value; + njs_vmcode_object_copy_t *copy; index = node->u.variable->index; value = njs_variable_value(parser, index); @@ -333,12 +338,12 @@ njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) return node->index; } - njs_generate_code(parser, njs_vmcode_function_copy_t, copy); - copy->code.operation = njs_vmcode_function_copy; + njs_generate_code(parser, njs_vmcode_object_copy_t, copy); + copy->code.operation = njs_vmcode_object_copy; copy->code.operands = NJS_VMCODE_2OPERANDS; copy->code.retval = NJS_VMCODE_RETVAL; copy->retval = node->index; - copy->function = index; + copy->object = index; return NXT_OK; } @@ -347,6 +352,31 @@ njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node) } +static nxt_int_t +njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser, + njs_parser_node_t *node) +{ + njs_index_t index; + njs_vmcode_object_copy_t *copy; + + index = node->index; + + node->index = njs_generator_dest_index(vm, parser, node); + if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) { + return node->index; + } + + njs_generate_code(parser, njs_vmcode_object_copy_t, copy); + copy->code.operation = njs_vmcode_object_copy; + copy->code.operands = NJS_VMCODE_2OPERANDS; + copy->code.retval = NJS_VMCODE_RETVAL; + copy->retval = node->index; + copy->object = index; + + return NXT_OK; +} + + static nxt_int_t njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node) { diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c index a632b6c3..ff9c5c88 100644 --- a/njs/njs_lexer_keyword.c +++ b/njs/njs_lexer_keyword.c @@ -73,6 +73,7 @@ static const njs_keyword_t njs_keywords[] = { /* Builtin objects. */ { nxt_string("this"), NJS_TOKEN_THIS, 0 }, + { nxt_string("Math"), NJS_TOKEN_MATH, 0 }, /* Builtin functions. */ diff --git a/njs/njs_math.c b/njs/njs_math.c new file mode 100644 index 00000000..7beada50 --- /dev/null +++ b/njs/njs_math.c @@ -0,0 +1,554 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static njs_ret_t +njs_object_math_abs(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, fabs(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_acos(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, acos(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_asin(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, asin(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_atan(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, atan(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_atan2(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double y, x; + + y = NJS_NAN; + x = NJS_NAN; + + if (nargs > 1) { + y = args[1].data.u.number; + } + + if (nargs > 2) { + x = args[2].data.u.number; + } + + njs_number_set(&vm->retval, atan2(y, x)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_ceil(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, ceil(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_cos(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, cos(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_exp(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, exp(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_floor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, floor(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_log(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, log(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_max(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + nxt_uint_t i; + + if (nargs > 1) { + for (i = 1; i < nargs; i++) { + if (!njs_is_numeric(&args[i])) { + vm->frame->trap_scratch.data.u.value = &args[i]; + return NJS_TRAP_NUMBER_ARG; + } + } + + num = args[1].data.u.number; + + for (i = 2; i < nargs; i++) { + num = fmax(num, args[i].data.u.number); + } + + } else { + num = -NJS_INFINITY; + } + + njs_number_set(&vm->retval, num); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_min(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + nxt_uint_t i; + + if (nargs > 1) { + for (i = 1; i < nargs; i++) { + if (!njs_is_numeric(&args[i])) { + vm->frame->trap_scratch.data.u.value = &args[i]; + return NJS_TRAP_NUMBER_ARG; + } + } + + num = args[1].data.u.number; + + for (i = 2; i < nargs; i++) { + num = fmin(num, args[i].data.u.number); + } + + } else { + num = NJS_INFINITY; + } + + njs_number_set(&vm->retval, num); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_pow(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double base, exponent; + + base = NJS_NAN; + exponent = NJS_NAN; + + if (nargs > 1) { + base = args[1].data.u.number; + } + + if (nargs > 2) { + exponent = args[2].data.u.number; + } + + njs_number_set(&vm->retval, pow(base, exponent)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_round(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, round(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_sin(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, sin(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_sqrt(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, sqrt(num)); + + return NXT_OK; +} + + +static njs_ret_t +njs_object_math_tan(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, + njs_index_t unused) +{ + double num; + + if (nargs > 1) { + num = args[1].data.u.number; + + } else { + num = NJS_NAN; + } + + njs_number_set(&vm->retval, tan(num)); + + return NXT_OK; +} + + +static const njs_object_prop_t njs_math_object_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("E"), + .value = njs_value(NJS_NUMBER, 1, 2.718281828459045), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("LN10"), + .value = njs_value(NJS_NUMBER, 1, 2.302585092994046), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("LN2"), + .value = njs_value(NJS_NUMBER, 1, 0.6931471805599453), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("LOG10E"), + .value = njs_value(NJS_NUMBER, 1, 0.4342944819032518), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("LOG2E"), + .value = njs_value(NJS_NUMBER, 1, 1.4426950408889634), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("PI"), + .value = njs_value(NJS_NUMBER, 1, 3.141592653589793), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("SQRT1_2"), + .value = njs_value(NJS_NUMBER, 1, 0.7071067811865476), + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("SQRT2"), + .value = njs_value(NJS_NUMBER, 1, 1.4142135623730951), + }, + + { + .type = NJS_NATIVE_GETTER, + .name = njs_string("__proto__"), + .value = njs_native_getter(njs_object_prototype_get_proto), + }, + + { + .type = NJS_METHOD, + .name = njs_string("abs"), + .value = njs_native_function(njs_object_math_abs, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("acos"), + .value = njs_native_function(njs_object_math_acos, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("asin"), + .value = njs_native_function(njs_object_math_asin, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("atan"), + .value = njs_native_function(njs_object_math_atan, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("atan2"), + .value = njs_native_function(njs_object_math_atan2, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("ceil"), + .value = njs_native_function(njs_object_math_ceil, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("cos"), + .value = njs_native_function(njs_object_math_cos, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("exp"), + .value = njs_native_function(njs_object_math_exp, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("floor"), + .value = njs_native_function(njs_object_math_floor, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("log"), + .value = njs_native_function(njs_object_math_log, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("max"), + .value = njs_native_function(njs_object_math_max, 0, 0), + }, + + { + .type = NJS_METHOD, + .name = njs_string("min"), + .value = njs_native_function(njs_object_math_min, 0, 0), + }, + + { + .type = NJS_METHOD, + .name = njs_string("pow"), + .value = njs_native_function(njs_object_math_pow, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("round"), + .value = njs_native_function(njs_object_math_round, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("sin"), + .value = njs_native_function(njs_object_math_sin, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("sqrt"), + .value = njs_native_function(njs_object_math_sqrt, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, + + { + .type = NJS_METHOD, + .name = njs_string("tan"), + .value = njs_native_function(njs_object_math_tan, 0, + NJS_SKIP_ARG, NJS_NUMBER_ARG), + }, +}; + + +const njs_object_init_t njs_math_object_init = { + njs_math_object_properties, + nxt_nitems(njs_math_object_properties), +}; diff --git a/njs/njs_math.h b/njs/njs_math.h new file mode 100644 index 00000000..11f8c169 --- /dev/null +++ b/njs/njs_math.h @@ -0,0 +1,17 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NJS_MATH_H_INCLUDED_ +#define _NJS_MATH_H_INCLUDED_ + + +#include + + +extern const njs_object_init_t njs_math_object_init; + + +#endif /* _NJS_MATH_H_INCLUDED_ */ diff --git a/njs/njs_object.c b/njs/njs_object.c index 8da185ac..71f312f7 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -34,6 +34,31 @@ njs_object_alloc(njs_vm_t *vm) nxt_lvlhsh_init(&object->hash); nxt_lvlhsh_init(&object->shared_hash); object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT]; + object->shared = 0; + } + + return object; +} + + +njs_object_t * +njs_object_value_copy(njs_vm_t *vm, njs_value_t *value) +{ + njs_object_t *object; + + object = value->data.u.object; + + if (!object->shared) { + return object; + } + + object = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_t)); + + if (nxt_fast_path(object != NULL)) { + *object = *value->data.u.object; + object->__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT]; + object->shared = 0; + value->data.u.object = object; } return object; @@ -51,6 +76,7 @@ njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, nxt_uint_t type) if (nxt_fast_path(ov != NULL)) { nxt_lvlhsh_init(&ov->object.hash); nxt_lvlhsh_init(&ov->object.shared_hash); + ov->object.shared = 0; index = njs_primitive_prototype_index(type); ov->object.__proto__ = &vm->prototypes[index]; @@ -413,7 +439,7 @@ const njs_object_init_t njs_object_constructor_init = { }; -static njs_ret_t +njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value) { njs_object_t *proto; diff --git a/njs/njs_object.h b/njs/njs_object.h index 87bdc57f..4ab6ed08 100644 --- a/njs/njs_object.h +++ b/njs/njs_object.h @@ -44,6 +44,7 @@ struct njs_object_init_s { njs_object_t *njs_object_alloc(njs_vm_t *vm); +njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value); njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, nxt_uint_t type); njs_object_prop_t *njs_object_property(njs_vm_t *vm, njs_object_t *obj, @@ -55,6 +56,7 @@ njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args, njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name); njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value); +njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value); njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 39bf3ce0..636c0ef3 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -68,6 +68,8 @@ static njs_token_t njs_parser_throw_statement(njs_vm_t *vm, njs_parser_t *parser); static njs_token_t njs_parser_grouping_expression(njs_vm_t *vm, njs_parser_t *parser); +static njs_token_t njs_parser_builtin_object(njs_vm_t *vm, njs_parser_t *parser, + njs_parser_node_t *node); static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj); static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, @@ -1470,7 +1472,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) break; } - parser->code_size += sizeof(njs_vmcode_function_copy_t); + parser->code_size += sizeof(njs_vmcode_object_copy_t); node->lvalue = NJS_LVALUE_ENABLED; node->u.variable = var; break; @@ -1588,6 +1590,9 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) node->index = NJS_INDEX_THIS; break; + case NJS_TOKEN_MATH: + return njs_parser_builtin_object(vm, parser, node); + case NJS_TOKEN_OBJECT_CONSTRUCTOR: node->index = NJS_INDEX_OBJECT; break; @@ -1631,6 +1636,36 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) } +static njs_token_t +njs_parser_builtin_object(njs_vm_t *vm, njs_parser_t *parser, + njs_parser_node_t *node) +{ + nxt_uint_t index, level; + njs_value_t *value; + njs_variable_t *var; + + var = njs_parser_variable(vm, parser, &level); + if (nxt_slow_path(var == NULL)) { + return NJS_TOKEN_ERROR; + } + + var->state = NJS_VARIABLE_DECLARED; + node->index = var->index; + + value = njs_variable_value(parser, node->index); + + index = node->token - NJS_TOKEN_FIRST_OBJECT; + value->data.u.object = &vm->shared->objects[index]; + value->type = NJS_OBJECT; + value->data.truth = 1; + + parser->node = node; + parser->code_size += sizeof(njs_vmcode_object_copy_t); + + return njs_lexer_token(parser->lexer); +} + + static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj) { diff --git a/njs/njs_parser.h b/njs/njs_parser.h index a0a56837..cd16e335 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -159,6 +159,10 @@ typedef enum { NJS_TOKEN_THIS, +#define NJS_TOKEN_FIRST_OBJECT NJS_TOKEN_MATH + + NJS_TOKEN_MATH, + NJS_TOKEN_OBJECT_CONSTRUCTOR, NJS_TOKEN_ARRAY_CONSTRUCTOR, NJS_TOKEN_FUNCTION_CONSTRUCTOR, diff --git a/njs/njs_regexp.c b/njs/njs_regexp.c index cfc81146..608a2f45 100644 --- a/njs/njs_regexp.c +++ b/njs/njs_regexp.c @@ -353,6 +353,7 @@ njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern) nxt_lvlhsh_init(®exp->object.hash); nxt_lvlhsh_init(®exp->object.shared_hash); regexp->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_REGEXP]; + regexp->object.shared = 0; regexp->last_index = 0; regexp->pattern = pattern; } diff --git a/njs/njs_vm.c b/njs/njs_vm.c index d9fc903a..9d1df6df 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -416,34 +416,6 @@ njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) } -njs_ret_t -njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld) -{ - njs_function_t *function; - - if (njs_is_function(value)) { - - function = njs_function_value_copy(vm, value); - - if (nxt_fast_path(function != NULL)) { - vm->retval.data.u.function = function; - vm->retval.type = NJS_FUNCTION; - vm->retval.data.truth = 1; - - return sizeof(njs_vmcode_function_copy_t); - } - - return NXT_ERROR; - } - - vm->retval = *value; - - njs_retain(value); - - return sizeof(njs_vmcode_function_copy_t); -} - - njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) { @@ -466,6 +438,42 @@ njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) } +njs_ret_t +njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld) +{ + njs_object_t *object; + njs_function_t *function; + + switch (value->type) { + + case NJS_OBJECT: + object = njs_object_value_copy(vm, value); + if (nxt_slow_path(object == NULL)) { + return NXT_ERROR; + } + + break; + + case NJS_FUNCTION: + function = njs_function_value_copy(vm, value); + if (nxt_slow_path(function == NULL)) { + return NXT_ERROR; + } + + break; + + default: + break; + } + + vm->retval = *value; + + njs_retain(value); + + return sizeof(njs_vmcode_object_copy_t); +} + + njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, njs_value_t *property) diff --git a/njs/njs_vm.h b/njs/njs_vm.h index b754cfb1..19a822aa 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -128,6 +128,8 @@ struct njs_object_s { /* An object __proto__. */ njs_object_t *__proto__; + + uint32_t shared; /* 1 bit */ }; @@ -148,11 +150,9 @@ struct njs_function_s { #if (NXT_64BIT) uint8_t native; uint8_t continuation_size; - uint8_t shared; #else uint8_t native; uint8_t continuation_size; - uint8_t shared; #endif union { @@ -278,11 +278,11 @@ union njs_value_s { .type = NJS_FUNCTION, \ .truth = 1, \ .u.function = & (njs_function_t) { \ + .object.shared = 1, \ .native = 1, \ .continuation_size = _size, \ .args_types = { __VA_ARGS__ }, \ .args_offset = 1, \ - .shared = 1, \ .u.native = _function, \ } \ } \ @@ -482,15 +482,15 @@ typedef struct { typedef struct { njs_vmcode_t code; njs_index_t retval; - njs_index_t function; -} njs_vmcode_function_copy_t; + njs_regexp_pattern_t *pattern; +} njs_vmcode_regexp_t; typedef struct { njs_vmcode_t code; njs_index_t retval; - njs_regexp_pattern_t *pattern; -} njs_vmcode_regexp_t; + njs_index_t object; +} njs_vmcode_object_copy_t; typedef struct { @@ -686,6 +686,12 @@ enum njs_functions_e { }; +enum njs_object_e { + NJS_OBJECT_MATH = 0, +#define NJS_OBJECT_MAX (NJS_OBJECT_MATH + 1) +}; + + #define njs_scope_index(value) \ ((njs_index_t) ((value) << NJS_SCOPE_SHIFT)) @@ -782,6 +788,8 @@ struct njs_vm_shared_s { nxt_lvlhsh_t values_hash; nxt_lvlhsh_t null_proto_hash; + njs_object_t objects[NJS_OBJECT_MAX]; + /* * The prototypes and functions arrays must be togther because they are * copied to njs_vm_t by single memcpy() in njs_builtin_objects_clone(). @@ -802,10 +810,10 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *inlvd2); njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); -njs_ret_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, - njs_value_t *invld); njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1, njs_value_t *invld2); +njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, + njs_value_t *invld); njs_ret_t njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, njs_value_t *property); diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index b83395fb..2ac4b133 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -3914,6 +3914,68 @@ static njs_unit_test_t njs_test[] = { nxt_string("eval()"), nxt_string("") }, + /* Math. */ + + { nxt_string("Math.PI"), + nxt_string("3.14159") }, + + { nxt_string("Math.E"), + nxt_string("2.71828") }, + + { nxt_string("Math.SQRT2"), + nxt_string("1.41421") }, + + { nxt_string("Math.abs(5)"), + nxt_string("5") }, + + { nxt_string("Math.abs(-5)"), + nxt_string("5") }, + + { nxt_string("Math.abs('5.0')"), + nxt_string("5") }, + + { nxt_string("Math.abs('abc')"), + nxt_string("NaN") }, + + { nxt_string("Math.max()"), + nxt_string("-Infinity") }, + + { nxt_string("Math.max(null)"), + nxt_string("0") }, + + { nxt_string("Math.max(undefined)"), + nxt_string("NaN") }, + + { nxt_string("Math.max('1', '2', '5')"), + nxt_string("5") }, + + { nxt_string("Math.min()"), + nxt_string("Infinity") }, + + { nxt_string("Math.min(null)"), + nxt_string("0") }, + + { nxt_string("Math.min(undefined)"), + nxt_string("NaN") }, + + { nxt_string("Math.min('1', '2', '5')"), + nxt_string("1") }, + + { nxt_string("Math.pow(2, 5)"), + nxt_string("32") }, + + { nxt_string("Math.pow(2)"), + nxt_string("NaN") }, + + { nxt_string("Math.pow()"), + nxt_string("NaN") }, + + /* ES5: Must be "[object Math]". */ + { nxt_string("Math"), + nxt_string("[object Object]") }, + + /* External interface. */ + { nxt_string("function f(req) { return req.uri }"), nxt_string("АБВ") }, -- 2.47.3